LoginSignup
8
6

Pythonで複数のリストを同時にfor文で回す

Last updated at Posted at 2020-03-21

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タグの要素にタイトルが記載されている
[<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部分・タイトル部分の文字列だけを抽出できるようになる。

urlとtitleを整形

    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

便利

8
6
0

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
8
6