やりたい事
見出し(h3タグ)・本文・画像へのリンク の3つがセットになり、それが複数セットあるページをスクレイピングして、それぞれ3つの要素を抜き出してCSV出力したいと考えています。
H3見出し以外 の本文、画像へのリンクは すべて Pタグなのでスクレイピングする要素が少なく困っていたところ、下記サンプルコードを公開頂いているページを見て、このコードを改良したらいけそう・・と思いました。
改良前サンプルコード
↓HTML から本文のテキストだけを抽出する Python コード例(見出しタグと見出しに属するテキストを取得)
上記サンプルコードでは、見出しと見出しに属するテキスト の2つセットをCSVにて出力しているのですが、
私の場合は、「見出し、見出しに属するテキスト、見出しに属する画像へのリンク」の3つをセットにして取得したいと考えました
取得するHTML
見出し
見出しに属する本文 の最後に
本文+画像リンクとなっている
<h3>いいい</h3> <p>本文テキスト4。</p> <p>本文テキスト5。<a href='https://******.com/***.html>https://*****.com/***.html</a></p> <h3>ううう</h3> <p>本文テキスト6。</p>
<p>本文テキスト6。</p>
<p>本文テキスト7 <a href='https://.com/.html>https://.com/*.html</a></p>
。</p>
</body> </html> ```</div>
下記のように自分で改良してみたのですが・・・
改良した部分には ■改良部分としています
python
1"""extract_text.py""" 2 3import re 4import csv 5from pathlib import Path 6from lxml import html 7 8 9# 不要なタグを検索する xpath 表現のタプル 10REMOVE_TAGS = ('.//style', './/script', './/noscript') 11 12# 見出しタグを検索する xpath 表現 13XPATH_H_TAGS = './/h1|.//h2|.//h3|.//h4|.//h5|.//h6' 14 15# 見出しタグを検出するための正規表現 16RE_H_MATCH = re.compile('^h[1-6]$').match 17 18 19def main(): 20 """メイン関数""" 21 22 # (1/8) HTML ファイルを指定します 23 src_file = Path(r'***\source.html') 24 25 # (2/8) HTML データを取得します 26 with src_file.open('rb') as f: 27 html_data = f.read() 28 29 # (3/8) HTML を解析します 30 root = html.fromstring(html_data) 31 32 # (4/8) HTML から不要なタグを削除します 33 for remove_tag in REMOVE_TAGS: 34 for tag in root.findall(remove_tag): 35 tag.drop_tree() 36 37 # (5/8) テキストの入れ物を用意します 38 # (デバッグ用にラベル行も追加) 39 texts = [] 40 texts.append( 41 ['タグ名', 'タグテキスト', 'タグに属するテキスト','タグに属する画像URL#■改良部分■']) 42 43 # (6/8) タイトルタグを取得します 44 t = root.find('.//title') 45 if t is not None: 46 text = t.text_content() 47 48 # 空でなければリストに追加 49 if text: 50 texts.append([t.tag, text, '']) 51 52 print(f'(デバッグ) {t.tag}: {text}\n') 53 54 # (7/8) 見出しタグを検索します 55 for h_tag in root.xpath(XPATH_H_TAGS): 56 # 見出しタグのテキストを取得 57 h_text = h_tag.text_content() 58 59 print(f'(デバッグ) {h_tag.tag}: {h_text}') 60 61 # 見出しタグと同じ階層にあったテキストを入れるリスト 62 contents = [] 63 #■改良部分■ リンクの変数定義 64 elem_link=[] 65 66 # 見出しの次のタグを取得 67 next_tag = h_tag.getnext() 68 69 # 次のタグがなくなるまでループ 70 while next_tag is not None: 71 # タグが見出しだったらブレーク 72 if RE_H_MATCH(next_tag.tag): 73 print(f'(デバッグ) 次の見出しタグ {next_tag.tag} が見つかった。') 74 print(f'(デバッグ) while ブレーク\n') 75 break 76 77 # タグのテキストを取得 78 text = next_tag.text_content() 79 80 # 空でなければリストに追加←#■改良部分■ リンクが存在しない行のみtextに追加 81 if 'http' not in text: 82 contents.append(text) 83 84 print(f'(デバッグ) {next_tag.tag}: {text}') 85 86 ##■改良部分■ httpがあればリンクの変数elem_linkに追加 87 if 'http' in text: 88 elem_link.append(text) 89 90 # さらに次のタグを取得してループする 91 next_tag = next_tag.getnext() 92 else: 93 # 同じ階層のタグをたどり尽くして、次のタグが無かった場合。 94 print(f'(デバッグ) 次のタグが無かった。 {next_tag}') 95 96 # リストを連結してひとつの文字列にします 97 contents = '|'.join(contents) 98 99 # リストに追加 #■改良部分■ 4列目にリンクの値のみを表示したいが 本文とセットになった行が表示される 100 texts.append([h_tag.tag, h_text, contents,elem_link]) 101 102 # (8/8) テキストを CSV に保存します 103 csv_file = Path(r'***\texts.csv') 104 with csv_file.open('w', encoding='utf-8', newline='') as f: 105 w = csv.writer(f) 106 w.writerows(texts) 107 108 # 以上です 109 return 110 111 112if __name__ == "__main__": 113 main()
要するに、元ソースは、見出しから見出しまでの間に存在する、同じ階層の行(text)をfor文で読み込みしていって、その行を結合して contents としていたのを、
改良後は、if文で http:(画像へのリンク)が含まれない行は 今までどおり contents で
行に http(画像へのリンク)が含まれる場合においては、リンクが含まれる行として elem_link の変数に入れるようにしました
リンクを含む本文は、elem_link の変数に入れる事ができたのですが、問題は このelem_linkから、href の値となる URLの値だけを抽出するのにはどうしたらいいのでしょうか?
現在の elem_link の中身
「本文テキスト3。<a href='https://.com/.html>https://.com/*.html</a>」
URLの値だけにしたい
「https://**.com/.html」
replace やsplit は、list に使えないと警告がでて、どの場面で リンクURLの値を抽出したらいいのかがわからないのですが・・ for文のリスト内の変数elem_link から特定の値を抽出する方法を教えていただけないでしょうか・・
回答2件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/08/08 05:33
2021/08/08 06:05 編集
2021/08/08 06:38
2021/08/08 06:40
2021/08/09 05:00 編集
2021/08/09 05:34
2021/08/09 06:09
2021/08/09 08:35