LoginSignup
18
25

More than 3 years have passed since last update.

PythonのScrapyの便利技まとめ

Last updated at Posted at 2019-12-06

PythonのScrapyはいろいろできるスクレイピング、クローリング専用のライブラリ

最近業務でスクレイピングの仕事がちょくちょく入るようになりました。
以前がsimplehtmlというPHPのライブラリでスクレイピングを実装していましたが

  • フォームの自動提出
  • 専用のCLIがある
  • 単に流行っている

という理由から最近はPythonのScrapyを使ってスクレイピング業務をしています
(PHPは楽ですがPHPからは卒業したいという個人的な思いもあります。)

Scrapyの奥が深いと思う理由

Scrapyがなぜよいかというと主に以下の理由からです

  • 複雑なスクレイピングを組むことができる
  • CLIのコマンドツールで試し打ちができる

が挙げられると思います。これまでスクレイピングはURLのパターンを読解してやっていましたが
Scrapyでは画面遷移させるためのメソッドが用意されていてseleniumでできるブラウザの自動化よりもはるかにすくないメモリのリソースで例えばフォーム提出などができます。

Scrapyで発見した便利技

基本

scrapyのインストール

$pip install scrapy

scrapyのSpiderのプロジェクトを始める

$scrapy startproject [project_name] [project_dir]

コマンドライン編

作成したSpiderのプロジェクトを一覧する

$scrapy list

作成したプロジェクトに新規のSpiderを作成する

#ドメイン名を追加する
$scrapy genspider [spider_name] mydomain.com

コマンドライン実行時にURLを指定する

$scrapy crawl -a start_urls="http://example1.com,http://example2.com" [spider_name]

CSVで出力する

$scrapy crawl -o csv_file_name.csv [spider_name]

JSONで出力する

$scrapy crawl -o json_file_name.json [spider_name]

シェル編

Scrapyシェルを起動する

$ scrapy shell [URL]

ページをすべて表示する

#responseは定義せず使える
response.body

リンクをすべて取得する

for link in response.css('a::attr(href)'):
   print link.get()

ライブラリ編

正規表現を使う

#aタグのhrefの中の特定のファイルがマッチした場合
matched = response.css('a::attr(href)').re(r'detail\.php')
if len(matched) > 0:
   print 'matched'

#aタグの文字列の中の特定の日本語がマッチした場合
matched = response.css('a::text').re(u'まとめ')
if len(matched) > 0:
   print 'matched'

タグを取得

#aタグを取得
response.css('a')

セレクタで取得

#aタグを取得
response.css('a.link')

#複数のクラスを取得<li class="page next"></li>
response.css('li.page.next')

相対パスをURLに変換する

for link in response.css('a::attr(href)'):
   print response.urljoin(link.get())

フォーム情報を送信

scrapy.FormRequest(response,formdata={"username":"login_username","password":"login_password"}

XPathで取得した要素の子要素の繰り返し処理

#DIV要素を取得
divs = response.xpath('//div')
#DIVの中のP要素を繰り返す
for p in divs.xpath('.//p'):  
     print(p.get())

別ページに遷移する

#self.parse(self,response)をコールバック関数として定義
yield scrapy.Request([url],callback=self.parse)

そのほか

Itemを作る(Project直下のitems.pyを編集)
元ネタ

class Product(scrapy.Item):
    name = scrapy.Field()
    price = scrapy.Field()
    stock = scrapy.Field()
    tags = scrapy.Field()
    last_updated = scrapy.Field(serializer=str)

サンプル

一覧のアイテムがなくなるまで詳細ページに遷移する(そのままでは動かないのでクラスの中に入れてください)

    def parse(self, response):
            title = a.css('::text').extract_first()
            title_match = a.css('::text').re(u'研修')
            if len(title_match) > 0:
                    "title":title,
                    "url":response.urljoin(link_param)
                }
                ptn = re.search("\/jinzaiikusei\/\w+\/",url)
                if ptn:
                    self.scraping_list.append(url)
        yield scrapy.Request(self.scraping_list[0],callback=self.parse_detail)
        pass

    def parse_detail(self, response):
         for item in response.css('a'):
             title =  item.css('::text').extract_first()
             url =  item.css('::attr(href)').extract_first()
             title_matched = item.css('::text').re(u'研修')
             url_matched = item.css('::attr(href)').re(r'jinzaiikusei\/.*\/.*\.html')
             if url_matched:
                 item = {
                         "title":title,
                         "url":url
                    }
                 yield item
         self.current_index = self.current_index + 1
         if self.current_index < len(self.scraping_list):

             yield scrapy.Request(self.scraping_list[self.current_index],callback=self.parse_detail)
         else:
             pass

更新履歴

  • 2019/12/06 新規作成
  • 2019/12/07 ライブラリの技を追加
  • 2019/12/09 ライブラリの技を追加(フォームの入力など)
  • 2019/12/16 itemsについての章を追加
  • 2019/12/21 コマンド編追記
  • 2020/1/20 シェルのパートに追記
  • 2020/2/12 urljoinを追加
  • 2020/2/13 サンプルを追加
18
25
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
18
25