zip
を使います。
あるサイトの記事一覧から、BeautifulSoup4
を使いURLとタイトルのリストを取得します。
目的は['url>>>title', 'url>>>title']
という、URLとタイトルを>>>
で接続したリストを作りたい。
以下のようにaタグの要素にタイトルが記載されているタイプのページなら...
import requests, bs4
res = requests.get('https://qiita.com/takuto_neko_like')
posts = bs4.BeautifulSoup(res.text, 'html.parser').select('.u-link-no-underline')
print(posts)
変数posts
には<class 'bs4.element.Tag'>
が入っており、posts[0]などとして個別にアクセスするとhtmlのaタグの内容を見られる。
[<a class="u-link-no-underline" href="/takuto_neko_like/items/52c6c52385386544aa62">herokuで悩んだところ</a>, <a class="u-link-no-underline" href="/takuto_neko_like/items/c5791f267e0964e09d03">新着記事を取得するツールを作った</a>, <a class="u-link-no-underline" href="/takuto_neko_like/items/93b3751984e5e3fd3670">fishの動きが遅すぎた gitのトラブル</a>, <a class="u-link-no-underline" href="/takuto_neko_like/items/62aeb4271614f6f0347f">Plotlyで作るグラフをDjangoで使う</a>, <a class="u-link-no-underline" href="/takuto_neko_like/items/c9c80ff453d0c4fad239">【Python】super()を使ってオーバーライドする理由</a>, <a class="u-link-no-underline" href="/takuto_neko_like/items/14e92797fa2b23a64adb">【Python】多重継承で継承するものは何?</a>, <a class="u-link-no-underline" href="/takuto_neko_like/items/6cf9bade3d9515a724c0">【Python】@classmethod及びデコレータとは?</a>, <a class="u-link-no-underline" href="/takuto_neko_like/items/aed9dd5619d8457d4894">【Python】*args **kwrgs って何だろう</a>, <a class="u-link-no-underline" href="/takuto_neko_like/items/bb8d0957347636b5bf4f">【Bootstrap】スクロールしてもnavbarを固定して表示する方法と、その際の留意点及び解決法</a>]
それぞれのaタグの内容は<class 'bs4.element.Tag'>
= Tagオブジェクトというものなので、str()で文字列型に変換することで.find()
で`タグ名や属性名などを指定して取得したインデックスを使い、URL部分・タイトル部分の文字列だけを抽出できるようになる。
for post in posts:
# URLを抽出
index_first = int(str(post).find('href=')) + 6
index_end = int(str(post).find('">'))
url = (str(post)[index_first : index_end])
# タイトルを抽出
index_first = int(str(post).find('">')) + 2
index_end = int(str(post).find('</a'))
title = (str(post)[index_first : index_end].replace('\u3000', ' '))
url_title_set.append(f"{url}>>>{title}")
などとすれば、出来上がり。
ただ、aタグの要素としてタイトルが記載されていないタイプのサイトもよくあります。
例えば画像とタイトルから成るカードとして記事情報が表示され、カード全体にリンクが貼ってあるパターン
<div class='card'><a href='#' class='link'>
<div class='image'><img src='#'></div>
<div class='title'>タイトル</div>
</a>
</div>
このような場合、bs4の.select
でクラスcard
を指定すると、cardクラスが適用されているdivタグ内を取得できます。そこからaタグのhref情報、titleクラスdivの要素を取得したい。
実際のコードではより多くの要素が重なっているので、親要素から.findで特定の文字列を探し出そうとすると、若干面倒くさい。
以下のようにそれぞれを個別に取得しました。
posts_links = bs4.BeautifulSoup(res.text, 'html.parser').select('.link')
posts_titles = bs4.BeautifulSoup(res.text, 'html.parser').select('.title')
先程のコードurlとtitleを整形
でpostsリストをfor文で回して`Tagオブジェクトに個別にアクセスしていきますが、今回はリストが2つあります。2つのリストを同時にfor文で回して、それぞれのリストから取得したurlとtitleを結合。その後新たなリストに格納したい。
このように、複数のリストを同時にfor文で回したいときは、zip
を使います。
now_posts_link_title_set = []
for (posts_link, posts_title) in zip(posts_links, posts_titles):
index_first = int(str(posts_link).find('href=')) + 6
index_end = int(str(posts_link).find('">'))
posts_link_set = (str(posts_link)[index_first : index_end])
index_first = int(str(posts_title).find('h2')) + 3
index_end = int(str(posts_title).find('</h2'))
posts_title_set = (str(posts_title)[index_first : index_end].replace('\u3000', ' ')) # 空白置換
now_posts_link_title_set.append(f"{posts_link_set}>>>{posts_title_set}")
2つより多くても大丈夫
for (a, b, c, d) in zip(a_list, b_list, c_list, d_list)
リストの要素数に差がある場合は、多い方が無視されます
aa = [1,2,3,4,5]
bb = ['a', 'b', 'c']
for (a, b) in zip(aa, bb):
print(f'{a} : {b}')
# 結果
1 : a
2 : b
3 : c
便利