Python webスクレイピング/①google NEWSトップページ/②googleニュース検索エンジン/③画像

■本記事はスクレイピングに関して、3つのことをします。

google NEWSトップページにあるリンクをスクレイピングする

googleニュース検索エンジンにキーワード検索をした結果をスクレイピングする

下図のように、タイトルとURLを抽出する。実行する度に、実行した時間をファイル名に記載する仕様であるため、実行の度にファイルが蓄積されてゆくことになる。

f:id:HK29:20191006233717p:plain

③キーワード検索で画像を抽出する(下図)。実行時の抽出するファイル数は指定できる。また、フォルダ名は実行した時間になるため、こちらも実行の度に上書きされることはない。

f:id:HK29:20191006233817p:plain

■インストールする必要があるPythonライブラリ4つ。condaもしくはpipでインストールする。

pip install requests
pip install beautifulsoup4
conda install feedparser
pip install google_images_download

ここで、次の1~4に注意する。画像をスクレイピングするために、google_images_downloadを使用しますが、通常では1回100抽出までの制約条件がある。この制限を回避するために、Chromeブラウザの機能を使用するため、ChromeとChromeDriverをインストールする必要があるが、ChromeとChromeDriverのverが合致しておく必要がある。 

▼1. Chromeのバージョンをブラウザのヘルプで確認。この場合、77

f:id:HK29:20191006235456p:plain

▼2. ChromeDriverのverを77を落とす

https://chromedriver.chromium.org/downloads

▼3. zipファイルなので解凍するとexeファイルがあるので、適当な場所に置く(下図)。

f:id:HK29:20191006235304p:plain

▼4. 本プログラム中でパスを通す

"chromedriver": "C:\Program Files (x86)\chromedriver_win32\chromedriver.exe",

 

■本プログラム

上記の準備をしておけば、コピペで実行可能です。

特記事項は下記2点です。

1. google ニュースのトップページのスクレイピングに関しては、アルゴリズムの関係上、time.sleep(1)を設定して相手側のサーバーに負荷を掛けないようにしています

2. UnicodeEncodeError: 'cp932' codec can't encode character '\u2661' in position 84: illegal multibyte sequence 等の文字コードエラーの対策として、encoding="utf-8_sig"  もしくは encoding='utf-16' をデータの読み書きする際に使用しています

#!/usr/bin/env python3
# coding: utf-8

import os, glob
import time, datetime
import pprint, csv
import pandas as pd

import requests
from bs4 import BeautifulSoup
import feedparser
from google_images_download import google_images_download

now = datetime.datetime.now()
now = now.strftime("%y%m%d_%H%M%S")

class Scraper:
    def __init__(self, site):
        self.site = site
    
    # googleニュースのトップページをスクレイピング
    def google_news(self, max_num):
        r = requests.get(self.site)
        sp = BeautifulSoup(r.content, "html.parser")
        news = []
        buf = ""
        n = 1
        for tag in sp.find_all("a"):
            url = tag.get("href")
            if url and "./articles" in url:
                if url != buf:
                    buf = url
                    url2 = url.replace("./articles", "https://news.google.com/articles")
                    r2 = requests.get(url2)
                    sp2 = BeautifulSoup(r2.content, "html.parser")
                    time.sleep(1)
                    title2 = sp2.title.string
                    print(n)
                    print(title2)
                    print(url2)
                    buf2 = [title2, url2]
                    news.append(buf2)
                    n += 1
                    if n==max_num:
                        break
        print("news[]")
        print(str(news))
        df = pd.DataFrame(news, columns=['title', 'link'])
        df = df.replace('\uff0d', 'hoge1').replace('\xa0', 'hoge2')
        df2 = df.dropna(subset=['title', 'link'])
        df3 = df2.drop_duplicates('link', keep='first')
        df3.to_csv(now + "_output_google_news.csv", header=True, index=False, encoding="utf-8_sig")

    # googleニュースの検索エンジンで検索した結果をスクレイピング
    def google_search(self, keyword):
        r = requests.get(self.site)
        print(r.url)
        d = feedparser.parse(r.url)
        news = []
        for i, entry in enumerate(d.entries, 1):
            p = entry.published_parsed
            sortkey = "%04d%02d%02d%02d%02d%02d" % (p.tm_year, p.tm_mon, p.tm_mday, p.tm_hour, p.tm_min, p.tm_sec)
            buf = {
                "no": i,
                "sortkey": sortkey,
                "published": entry.published,
                "title": entry.title,
                "link": entry.link
            }
            news.append(buf)
        news = sorted(news, key=lambda x: x['sortkey'], reverse=True)
        pprint.pprint(news)
        with open(now + '_output_google_news_search_' + str(keyword.split()) + '.csv', 'w', newline='', encoding='utf-16') as f:
            fieldnames = news[0].keys()
            writer = csv.DictWriter(f, fieldnames=fieldnames)
            writer.writeheader()
            for e in news:
                writer.writerow(e)

# google_images_downloadを利用して、指定した画像枚数をスクレイピング
def scrape_images(keyword, max_num):
    #buf = '_'.join(keyword.split())
    dir_name = now + "_" + str(keyword.split())
    config = {
        "Records":[
            {
            "keywords": keyword,
            "limit": max_num,
            "no_numbering": True,
            "output_directory": "images",
            "image_directory": dir_name,
            "chromedriver": "C:\Program Files (x86)\chromedriver_win32\chromedriver.exe",
            },
        ]
    }
    res = google_images_download.googleimagesdownload()
    for rc in config["Records"]:
        res.download(rc)

if __name__ == "__main__":
    ### グーグルニュースのトップページをスクレイピング
    
    max_num = 20 # 抽出数
    urlA = "https://news.google.com/?hl=ja&gl=JP&ceid=JP:ja"
    scrapeA = Scraper(urlA)
    scrapeA.google_news(max_num)    
    
    ### グーグルニュースの検索エンジンで検索した結果をスクレイピング
    
    keyword = "株 AI"
    urlB = "https://news.google.com/news/rss/search/section/q/" + keyword + "/" + keyword + "?hl=ja&gl=JP&ned=jp"
    print(urlB)
    scrapeB = Scraper(urlB)
    scrapeB.google_search(keyword)
    
    ### google_images_downloadを利用して、指定した画像枚数をスクレイピング
    
    #keyword = "株 AI"
    max_num = 20 # 抽出数
    scrape_images(keyword, max_num)
    
    print("finished")
  

以上

<広告>