Qiitaのリンク情報を取得する(Python, Selenium, スクレイピング)

## はじめに
- 自分のQiitaの投稿記事に対して、LGTMやSTOCKがつくとうれしいです。ただ、それ以上に嬉しいのは、**他の方からの記事のリンク**をしていただくことである、と投稿してみて気がつきました。Qiitaに記事を投稿される方が「記事を参考にした」、ということですから、大なり小なり他の方の役に立った明確なサインかと思います。

- そこで、自分のQiita投稿について、他者からのリンク情報を取得してみたいと思います。QiitaAPIでは、投稿に対するリンク記事は取得できないと思います（たぶん）ので、自分の投稿についてスクレイピングをして情報を取得してみたいと思います。

## 普通にスクレイピングしてみる(python: requests + BeautifulSoup)

Pythonで普通(requests + BeautifulSoup)にスクレイピングをしてみると、データはとれません。なぜ取得できないのは下記のページがとても参考になります。
https://gammasoft.jp/blog/how-to-download-web-page-created-javascript/

~~~ scraping1.py
import urllib.request, urllib.error
from bs4 import BeautifulSoup

# アクセスするURL(自分のQiitaの記事)
url = 'https://qiita.com/yoshi_yast/items/521c1f36306a180f45dd'

html = urllib.request.urlopen(url)
soup = BeautifulSoup(html, "html.parser")
elems = soup.select('.css-1w5fqid')

print(elems)
~~~

結果：

~~~ 
[]
~~~

## Seleniumでやってみる(ブラウザのエンジンでHTMLを生成)

その対応方法としては、ブラウザのエンジンでHTMLを生成させる必要があります。Selenium WebDriverでブラウザを操作してみたいと思います。実行するとブラウザが起動します。

~~~ scraping2.py
import time
from selenium import webdriver
from bs4 import BeautifulSoup

# 仮想ブラウザ起動、URL先のサイトにアクセス(chromedriverの保存先を指定 windows環境)
driver_path = 'C:\\Users\\*************************\\chromedriver.exe'

# アクセスするURL(自分のQiitaの記事)
url = 'https://qiita.com/yoshi_yast/items/521c1f36306a180f45dd'

driver = webdriver.Chrome(executable_path = driver_path)
driver.get(url)

time.sleep(2)

soup = BeautifulSoup(driver.page_source, features="html.parser")
elems = soup.select('.css-1w5fqid')

for elem in elems:
    atag = elem.find('a')
    print(atag)
~~~

結果： リンクを取得できた

~~~
<a class="css-1okdvfi" href="https://qiita.com/nideratakumin/items/5574d9f38616a03f04c6">GoogleMapsAPI, GeocodingAPIを使って投稿内容から緯度経度情報を取得し地図を表示させる (and 地図から投稿を検索できるようにする)</a>
<a class="css-1okdvfi" href="https://qiita.com/MNK0910/items/eebc0a3c15ff4872d308">railsでGoogleMapAPIの導入</a>
<a class="css-1okdvfi" href="https://qiita.com/yoshi_yast/items/bb75d8fceb712f1f49d1">Pythonでジオコーディング(Geocoder/Googlemaps)</a>
<a class="css-1okdvfi" href="https://qiita.com/yoshi_yast/items/4f0be22bfcf2d63412ee">【個人開発】ランキング自動生成サイト(Python/Django)</a>
~~~


## ブラウザを起動せずに取得する

ブラウザを起動せずにSeleniumを動かしたい、と思っていたのですが、どうやるんだろう、というところが一番モヤモヤしていました。
[実践/現場のPythonスクレイピング(Qiita)](https://qiita.com/ulwlu/items/c84501993635c72540a7) を参考にさせていただくと、それが可能でした。今回の一番の収穫です。

~~~ scraping3.py
import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from bs4 import BeautifulSoup

# オプションの設定
op = Options()
op.add_argument("--disable-gpu")
op.add_argument("--disable-extensions")
op.add_argument("--proxy-server='direct://'")
op.add_argument("--proxy-bypass-list=*")
op.add_argument("--start-maximized")
op.add_argument("--headless")

# 仮想ブラウザ起動、URL先のサイトにアクセス(chromedriverの保存先を指定 windows環境)
driver_path = 'C:\\Users\\************************************\\chromedriver.exe'
# アクセスするURL(自分のQiitaの記事)
url = 'https://qiita.com/yoshi_yast/items/521c1f36306a180f45dd'

driver = webdriver.Chrome(executable_path = driver_path, chrome_options = op)
driver.get(url)

time.sleep(2)

soup = BeautifulSoup(driver.page_source, features="html.parser")
elems = soup.select('.css-1w5fqid')

for elem in elems:
    atag = elem.find('a')
    print(atag)
~~~

結果：取得できた　→ scraping2.pyと同じ結果


## 自分のQiita投稿に対して、自分以外からのリンクを取得してみる

自分の記事一覧をQiitaAPIにて取得し、ループしながらリンク情報を出力してみたいと思います。

~~~ scraping4.py
import time
import requests
import pprint
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from bs4 import BeautifulSoup

# Qiita アクセストークン
token = '*******************************'

# Qiita API設定
url = 'https://qiita.com/api/v2/authenticated_user/items?page=1&per_page=100'
headers = {'content-type': 'application/json',
           'Authorization': 'Bearer ' + token}

# データ取得・ソート
res = requests.get(url, headers=headers)
lists = res.json()

# オプションの設定
op = Options()
op.add_argument("--disable-gpu")
op.add_argument("--disable-extensions")
op.add_argument("--proxy-server='direct://'")
op.add_argument("--proxy-bypass-list=*")
op.add_argument("--start-maximized")
op.add_argument("--headless")

# 仮想ブラウザ起動、URL先のサイトにアクセス(chromedriverの保存先を指定 windows環境)
driver_path = 'C:\\Users\\******************************\\chromedriver.exe'
driver = webdriver.Chrome(executable_path = driver_path, chrome_options = op)

linklist = []

for list in lists:

    driver.get(list['url'])

    time.sleep(2)

    soup = BeautifulSoup(driver.page_source, features="html.parser")
    elems = soup.select('.css-1w5fqid')

    # リンク情報から「自分からのリンク以外」を取得し、タイトルとリンクを表示する
    count = 0
    for elem in elems:

        atag = elem.find('a')
        alink = atag.get('href')
        check = alink.find('yoshi_yast')

        if check < 0:
            count += 1

    linklist.append([list['title'], count])

pprint.pprint(linklist)
~~~

結果：投稿のそれぞれのリンク数を取得できた（自分のリンク以外）



## まとめ
- python: requests + BeautifulSoupでは取れない情報もSeleniumを使用することで取得することができました。
- Seleniumは、ブラウザを起動せずに実行することができることがわかりました。
- LGTMがたくさんある投稿はすばらしく、読む価値があるものだと思います。それとともに、他者からのリンクを多く持っている投稿にも注目してみるといいかもしれません。

## 他参考URL
・[Python + Selenium で Chrome の自動操作を一通り(Qiita)](https://qiita.com/memakura/items/20a02161fa7e18d8a693)
・[Pythonスクレイピング：JavaScriptで動的に出力されているページと静的ページ、それぞれに最適な異なる二つの手法](https://www.handsonplus.com/programming/python-scraping/)