Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Webスクレイピング - 小手先の技術 -

Webスクレイピング - 小手先の技術 -

もうなんでもよい...

TakesxiSximada

July 06, 2016
Tweet

More Decks by TakesxiSximada

Other Decks in Technology

Transcript

  1. PUT res = requests.put('http://127.0.0.1:8888') HEAD res = requests.head('http://127.0.0.1:8888') DELETE res

    = requests.delete('http://127.0.0.1:8888') OPTIONS res = requests.options('http://127.0.0.1:8888') その他
  2. POST /?test=1 HTTP/1.1 Host: 127.0.0.1:8888 Accept: */* Accept-Encoding: gzip, deflate

    User-Agent: python-requests/2.10.0 Connection: keep-alive Content-Length: 0 送信されたリクエスト
  3. POST / HTTP/1.1 Host: 127.0.0.1:8888 Connection: keep-alive Accept-Encoding: gzip, deflate

    User-Agent: python-requests/2.10.0 Accept: */* Content-Length: 6 Content-Type: application/x-www-form-urlencoded test=1 送信されたリクエスト
  4. ipdb> print(self.request.recv(1024).decode()) GET / HTTP/1.1 Host: 127.0.0.1:8888 Accept: */* Accept-Encoding:

    gzip, deflate User-Agent: sximada <- ここ Connection: keep-alive 同じ要領でヘッダ情報を書き換えることがで きる 送信されたリクエスト
  5. get-heaer.py import io import pycurl fp = io.BytesIO() curl =

    pycurl.Curl() curl.setopt(pycurl.URL, 'http://127.0.0.1:8888') curl.setopt(pycurl.WRITEHEADER, fp) curl.perform() 結果の取得 2 header
  6. 実⾏してみる $ python -i get-header.py >>> fp.seek(0) 0 >>> print(fp.read().decode())

    HTTP/1.1 200 OK Server: TornadoServer/4.2 Date: Sat, 02 Jul 2016 06:29:15 GMT Receive: GET Etag: "ed4018fbd5f2a3d40c683820df3862f67b322328" Content-Type: text/html; charset=UTF-8 Content-Length: 11 >>> WRITEHEADERを指定しないとsys.stdoutに書き出す 結果の取得 2 header
  7. get-body.py import io import pycurl fp = io.BytesIO() curl =

    pycurl.Curl() curl.setopt(pycurl.URL, 'http://127.0.0.1:8888') curl.setopt(pycurl.WRITEDATA, fp) curl.perform() 結果の取得 3 body
  8. 送信されるリクエスト GET / HTTP/1.1 Host: 127.0.0.1:8888 Range: bytes=100- User-Agent: PycURL/7.43.0

    libcurl/7.43.0 OpenSSL/1.0.2d zlib/1.2.8 Accept: */* 分割ダウンロード
  9. import io import pycurl import progressbar fp = io.BytesIO() curl

    = pycurl.Curl() curl.setopt(pycurl.URL, 'おおきなファイルのURL') # noqa curl.setopt(pycurl.NOPROGRESS, 0) curl.setopt(pycurl.WRITEDATA, fp) progress = progressbar.ProgressBar() def update(total_to_download, total_downloaded, total_to_upload, total_uploaded if total_to_download: percent = int(total_downloaded / total_to_download * 100) progress.update(percent) curl.setopt(pycurl.PROGRESSFUNCTION, update) try: progress.start() curl.perform() finally: progress.finish() プログレスバー
  10. サンプルコード import bs4 import requests res = requests.get('PyPIのURL') soup =

    bs4.BeautifulSoup(res.content, 'html.parser') package_names = [ elm.getText() for elm in soup.select( '#content table.list tr td a')] aタグのリストを取得したのちgetText()でtextNondeの値を取り出している PyPIを解析してみる
  11. サンプルコード from lxml import etree import requests xpath = '//div[@id="content"]//table[@class="list"]//tr/td/a/text()'

    res = requests.get('PyPIのURL') html = etree.HTML(res.content) package_name = html.xpath(xpath) print(package_name) XPATHではtextNondeを直接指定できるため、 beautifulsoup4のように要素を取 得してから textNondeを取る必要がない pypiを解析してみる
  12. Profileの作成 from selenium.webdriver import FirefoxProfile default_profile = { 'security.warn_entering_secure': False,

    'security.warn_entering_secure.show_once': True, 'security.warn_entering_weak': False, 'security.warn_entering_weak._show_once': True, 'security.warn_leaving_secure': False, 'security.warn_leaving_secure.show_once': True, 'security.warn_leaving_weak': False, 'security.warn_leaving_weak._show_once': True, 'security.warn_submit_insecure': False, 'security.warn_viewing_mixed': False, 'security.warn_viewing_mixed.show_once': True, } profile = FirefoxProfile() for name, value in default_profile.items(): profile.set_preference(name, value) Firefoxの起動
  13. 起動 from selenium.webdriver import Firefox browser = Firefox(firefox_profile=profile, proxy=proxy) #

    pageをloadするまでの待ち時間を設定 browser.implicitly_wait = 10 # Cookieを全消し browser.delete_allcookies() Firefoxの起動
  14. 通常の場合 親 ⼦ 孫 メモ SIGTERM -> handle SIGTERM ->

    handle 終了 SIGKILL -> handle 終了 PhantomJSを使った時の注意点
  15. 問題が発⽣する場合 親 ⼦ 孫 メモ SIGTERM -> handle SIGKILL ->

    handle ⼦がSIGTERMを孫に送る前にSIGKILL到達 終了 ZOMBIE PhantomJSを使った時の注意点
  16. Spider Webサイトをどのようにクロールするか responseをどのように扱うかを指定 class ExampleSpider(Spider): name = "example" allowed_domains =

    ["example.com"] start_urls = ( r'https://example.com/pypi?%3Aaction=rss', # ... (^^; ) def parse(self, response): feed = feedparser.parse(response.body) for record in feed.entries: release = Release(record) item = ReleaseItem() item['name'] = release.name item['version'] = release.version item['link'] = release.link item['summary'] = release.summary yield item
  17. Pipeline Spiderで⽣成したobjectを渡されて処理をする 例えばDBに保存したりする処理はここで⾏う class ExamplePipeline(object): @classmethod def from_crawler(cls, crawler): return

    cls( url=crawler.settings.get('DB_URL'), ) def __init__(self, url): self.url = url def open_spider(self, spider): self.datastore = create_datastore(self.url) def process_item(self, item, spider): self.datastore.register(item)