LoginSignup
3
3

More than 1 year has passed since last update.

[Python]ローカルのフォルダ内にあるHTMLソースファイルの全リンクを抽出

Last updated at Posted at 2023-02-09

経緯

ほぼ前回の続きです
webページのソースをディレクトリそのままにスクレイピング、

詳しく述べると、
https://www.hoge.com/huga/hoge/index.htmlhttps://www.hoge.com/huga/index.htmlのソースをまとめて取得するとき、
ドメイン名をトップディレクトリとし、

└── www.hoge.com
     └── huga 
          ├── hoge ── index.html
          └── index.html

のようにしてローカルに持ってきました

このHTMLソース群の中からリンク(aタグのhref要素やimgタグのsrc要素など)を抽出したいと考えました
スクレイピングする時にわざわざソースをローカルに落とさずに直で抽出すればいいじゃんというツッコミは無しでお願いします

とりあえず思いつくままに書く

from bs4 import BeautifulSoup
import os

def fileGetLink(path):
 if os.path.isdir(path):
        fileList = os.listdir(path)
        for file in fileList:
            fileGetLink(os.path.join(path, file))
 else:
  soup = BeautifulSoup(open(path, encoding='utf-8'), 'html.parser')     

  for link in soup.find_all("a"): # aタグを取得
        with open('./hogeLink.txt', "a", encoding='UTF-8', newline='\n') as f:
         if link.get("href") is None:
          continue     
         f.write(link.get("href") + "\n")

  for link in soup.find_all("img"): # imgタグを取得
        with open('./hogeLink.txt', "a", encoding='UTF-8', newline='\n') as f:
         if link.get("src") is None:
          continue     
         f.write(link.get("src") + "\n")

  for link in soup.find_all("link"): # linkタグを取得
        with open('./hogeLink.txt', "a", encoding='UTF-8', newline='\n') as f:
         if link.get("href") is None:
          continue     
         f.write(link.get("href") + "\n")

  for link in soup.find_all("script"): # scriptタグを取得
        with open('./hogeLink.txt', "a", encoding='UTF-8', newline='\n') as f:
         if link.get("src") is None:
          continue     
         f.write(link.get("src") + "\n")   

fileGetLink('./www.hoge.com')  

冗長だなあ

解説

def fileGetLink(path):
 if os.path.isdir(path):
        fileList = os.listdir(path)
        for file in fileList:
            fileGetLink(os.path.join(path, file))

再帰検索してます
ファイルだったら処理、フォルダだったら検索、というのを繰り返しています

 soup = BeautifulSoup(open(path, encoding='utf-8'), 'html.parser')     

  for link in soup.find_all("a"): # aタグを取得
        with open('./hogeLink.txt', "a", encoding='UTF-8', newline='\n') as f:
         if link.get("href") is None:
          continue     
         f.write(link.get("href") + "\n")

  for link in soup.find_all("img"): # imgタグを取得
        with open('./hogeLink.txt', "a", encoding='UTF-8', newline='\n') as f:
         if link.get("src") is None:
          continue     
         f.write(link.get("src") + "\n")

  for link in soup.find_all("link"): # linkタグを取得
        with open('./hogeLink.txt', "a", encoding='UTF-8', newline='\n') as f:
         if link.get("href") is None:
          continue     
         f.write(link.get("href") + "\n")

  for link in soup.find_all("script"): # scriptタグを取得
        with open('./hogeLink.txt', "a", encoding='UTF-8', newline='\n') as f:
         if link.get("src") is None:
          continue     
         f.write(link.get("src") + "\n")   

find_allでまとめて取得してhogeLink.txtに書き出す処理です
NoneTypeで止まることあるので引っ掛けてcontinueしています

反省と修正

冗長過ぎてダサい

find_allの部分が冗長だなあと自分でも思いました
Beautiful Soupの公式ドキュメントを見ると、
タグを配列にして複数条件いけるおーとのことです

Recall from Kinds of filters that the value to name can be a string, a regular expression, a list, a function, or the value True.

 soup = BeautifulSoup(open(path, encoding='utf-8'), 'html.parser')     
  for link in soup.find_all(["a","link"]): 
        with open('./hogeLink.txt', "a", encoding='UTF-8', newline='\n') as f:
         if link.get("href") is None:
          continue     
         f.write(link.get("href") + "\n")

  for link in soup.find_all(["script","img"]):
        with open('./hogeLink.txt', "a", encoding='UTF-8', newline='\n') as f:
         if link.get("src") is None:
          continue     
         f.write(link.get("src") + "\n")   

こんな感じでスッキリしました

再帰処理いらない説

os.walkというディレクトリ下のファイル全てを検索してくれる関数があるそうです
以下参考記事

def fileGetLink(path):
 for pathName, dirName, fileName in os.walk(path):
  for file in fileName:
   path = os.path.join(pathName,file)
   soup = BeautifulSoup(open(path, encoding='utf-8'), 'html.parser') 

コンパクトになりました

最終的な実装

from bs4 import BeautifulSoup
import os

def fileGetLink(path):
 for pathName, dirName, fileName in os.walk(path):
  for file in fileName:
   path = os.path.join(pathName,file)
   soup = BeautifulSoup(open(path, encoding='utf-8'), 'html.parser') 
    for link in soup.find_all(["a","link"): 
        with open('./hogeLink.txt', "a", encoding='UTF-8', newline='\n') as f:
         if link.get("href") is None:
          continue     
         f.write(link.get("href") + "\n")

    for link in soup.find_all(["script","img"]):
        with open('./hogeLink.txt', "a", encoding='UTF-8', newline='\n') as f:
         if link.get("src") is None:
          continue     
         f.write(link.get("src") + "\n") 

fileGetLink('./www.hoge.com') 

まとめ

以上のスクリプトを実行するとソース内のリンクがビャーっとテキストファイルにリンク群が書き出されていきます
webサイトの内部ファイル(画像やpdfなど)の存在チェックをしたい時などにオススメです
余談ですが、最初自分の手癖で書いて、あとから公式ドキュメントなどを調査してコードをブラッシュアップしていく方式がすごく良かったので、皆様も是非やってみて下さい
(誤り等あれば指摘コメント下さい)

3
3
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3