スクレイピングを学んでいるときのメモ①~Requests,lxml~

最近スクレイピングを学んでいるのだが、使ったことないライブラリの使用が増えてきたので、使い方のメモを備忘録として残しておく。

ちなみに教材は以下。

増補改訂版もあるので今買うならこっち。

Requestsライブラリ

response = requests.get(url)

上記の使い方で特定のURLのResponseオブジェクトを取得できる。
Responseオブジェクトには以下の属性やメソッドがある。

import requests

response = requests.get('https://gihyo.jp/dp')
print(dir(response))
 #結果 
['__attrs__', '__bool__', '__class__', '__delattr__', '__dict__',
 '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__',
 '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__',
 '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__',
 '__module__', '__ne__', '__new__', '__nonzero__', '__reduce__',
 '__reduce_ex__', '__repr__', '__setattr__', '__setstate__',
 '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_content',
 '_content_consumed', '_next', 'apparent_encoding', 'close', 'connection',
 'content', 'cookies', 'elapsed', 'encoding', 'headers', 'history',
 'is_permanent_redirect', 'is_redirect', 'iter_content', 'iter_lines',
 'json', 'links', 'next', 'ok', 'raise_for_status', 'raw', 'reason',
 'request', 'status_code', 'text', 'url']

よく使うものだけ例を示す。

import requests

response = requests.get('https://gihyo.jp/dp')
print(response.status_code) #200 
print(response.headers) #結果 
{'Date': 'Sat, 18 Apr 2020 02:42:02 GMT',
 'Content-Type': 'text/html; charset=UTF-8',
 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive',
 'Set-Cookie': '__cfduid=d39c02e3e3825eb074c37d122ba4d44ad1587177722; expires=Mon,18-May-20 02:42:02 GMT; path=/; domain=.gihyo.jp; HttpOnly; SameSite=Lax,SN4dc8937382ea6=9-O1fcg30miggMj2On1dwqWEWnc; expires=Sat,18-Apr-2020 04:42:02 GMT; Max-Age=7200; path=/; HttpOnly',
 'Vary': 'Accept-Encoding',
 'Expires': 'Thu, 19 Nov 1981 08:52:00 GMT',
 'Cache-Control': 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0',
 'Pragma': 'no-cache',
 'X-FRAME-OPTIONS': 'SAMEORIGIN',
 'X-XSS-Protection': '1; mode=block',
 'Content-Encoding': 'gzip',
 'CF-Cache-Status': 'DYNAMIC',
 'Expect-CT': 'max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"',
 'Server': 'cloudflare',
 'CF-RAY': '585b07be3c14d63d-NRT',
 'alt-svc': 'h3-27=":443"; ma=86400, h3-25=":443"; ma=86400, h3-24=":443"; ma=86400, h3-23=":443"; ma=86400',
 'cf-request-id': '022cc32adf0000d63d93844200000001'}

status_codeはエラー判定に使えそう。
headersは辞書型のように使えるので、必要な情報を選択して使うのがよさそう。
textはhtmlがstr型で格納されている。

requestsにはSessionクラスがあり、sessionインスタンスでrequestsのgetメソッドと同じようにresponseを得られる。
sessionを使ってリクエストを送るとHTTP Keep-Aliveを使って接続するので、複数のリクエストを送るときはサーバ側の負荷を軽減できる。

import requests

session = requests.Session()
print(session.headers)
#{'User-Agent': 'python-requests/2.22.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
response = session.get('https://gihyo.jp/dp')
print(response.status_code)
print(response.headers)
print(response.text)

lxmlライブラリ

htmlからデータを抜き出す際に用いる。

import lxml.html
import requests

session = requests.Session()
response = session.get('https://gihyo.jp/dp')
html = response.text
print(type(html))
#<class 'str'>
root = lxml.html.fromstring(html)
print(type(root))
#<class 'lxml.html.HtmlElement'>

まず先ほど紹介したsessionを用いた方法でhtmlを取得する。それからlxml.html.fromstring()を使って<class 'lxml.html.HtmlElement'>オブジェクトのhtmlを取得している。

lxml.html.HtmlElementから特定の要素を抜き出すメソッドにはcssselectメソッドとxpathメソッドがある。
ここではcssselectメソッドのみを見ていく。たとえばhtmlのa要素だけを抜き出したいときは下記のように用いる。

import lxml.html
import requests

base_url = 'https://gihyo.jp'
session = requests.Session()
response = session.get(base_url)
html = response.text
root = lxml.html.fromstring(html)
print(type(root.cssselect("a")[0]))
#<class 'lxml.html.HtmlElement'>

lxml.html.HtmlElementオブジェクトとして抜き出せている。この要素に具体的にどのようなものが格納されているかはattribで確認できる。

import lxml.html
import requests

base_url = 'https://gihyo.jp'
session = requests.Session()
response = session.get(base_url)
html = response.text
root = lxml.html.fromstring(html)
for a in root.cssselect("a"):
   print(a.attrib)
 #結果 
{'href': '/'}
{'href': '/site/inquiry'}
{'href': '/site/profile'}
{'href': '/dev', 'title': 'DEVELOPER STAGE'}
{'href': '/admin', 'title': 'ADMINISTRATOR STAGE'}
...

hrefというkeyでbase_urlからの相対urlが格納されていることと、ものによってはtitleというkeyで値が格納されている。上の例にある{'href': '/dev', 'title': 'DEVELOPER STAGE'}が具体的にhtmlのどの部分から抽出しているかを見てみると、以下の部分であることがわかる。

画像1

<a href="/dev" title="DEVELOPER STAGE">デベロッパ</a>

この場合、データを抜き出すときは「デベロッパ」も併せて抜き出したい。これはa.textで抜き出すことが出来る。そこで最終的には以下のようなコードにした。

from urllib.parse import urljoin
import lxml.html
import requests

base_url = 'https://gihyo.jp'
session = requests.Session()
response = session.get(base_url)
html = response.text
root = lxml.html.fromstring(html)
for a in root.cssselect("a"):
   print(urljoin(base_url,a.get("href")),a.get("title"),a.text) #結果 
https://gihyo.jp/ None None
https://gihyo.jp/site/inquiry None お問い合わせ
https://gihyo.jp/site/profile None 会社案内
https://gihyo.jp/dev DEVELOPER STAGE デベロッパ
https://gihyo.jp/admin ADMINISTRATOR STAGE アドミニストレータ
...

先ほど確認したようにaは実質dict型なので、get()でvalueを取得できる。
また、urlは相対パスだと不便なので絶対パスに変換しておく。

この記事が気に入ったらサポートをしてみませんか?