PythonのScrapyの便利技まとめ

##PythonのScrapyはいろいろできるスクレイピング、クローリング専用のライブラリ
最近業務でスクレイピングの仕事がちょくちょく入るようになりました。
以前が[simplehtml](https://simplehtmldom.sourceforge.io/)というPHPのライブラリでスクレイピングを実装していましたが

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

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

##Scrapyの奥が深いと思う理由

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

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

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

###基本

***scrapyのインストール***

``` bash
$pip install scrapy
```

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

``` bash
$scrapy startproject [project_name] [project_dir]
```
###コマンドライン編

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

``` bash
$scrapy list
```
***作成したプロジェクトに新規のSpiderを作成する***

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

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

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

```



***CSVで出力する***

``` bash
$scrapy crawl -o csv_file_name.csv [spider_name]
```

***JSONで出力する***

``` bash
$scrapy crawl -o json_file_name.json [spider_name]
```

###シェル編

**Scrapyシェルを起動する**

``` bash
$ scrapy shell [URL]
```

**ページをすべて表示する**

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

**リンクをすべて取得する**

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

###ライブラリ編

**正規表現を使う**

``` python
#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'
```

**タグを取得**

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

**セレクタで取得**

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

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

```

**相対パスをURLに変換する**

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



**フォーム情報を送信**

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

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

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

**別ページに遷移する**

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

```

##そのほか

**Itemを作る(Project直下のitems.pyを編集)**
[元ネタ](https://doc.scrapy.org/en/latest/topics/items.html)

``` python
class Product(scrapy.Item):
    name = scrapy.Field()
    price = scrapy.Field()
    stock = scrapy.Field()
    tags = scrapy.Field()
    last_updated = scrapy.Field(serializer=str)
```
##サンプル

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

``` python
    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 サンプルを追加