スクレイピングのサンプル


## yahoo!ニュースのタイトルを取得

```python
from urllib.request import urlopen
from bs4 import BeautifulSoup
from pprint import pprint

URL = 'http://news.yahoo.co.jp/'
with urlopen(URL) as res:
  html = res.read().decode("utf-8")
    
soup = BeautifulSoup(html, 'html.parser')

titles = soup.select('.ttl a') # domを取得
titles = [t.contents[0] for t in titles] # テキストを取得
pprint(titles)
```

```bash:出力結果
>>>
['トランプ氏「司法行き過ぎ」',
 'PKO日報 特別防衛監察を指示',
 '病院で投与ミス 一時心肺停止',
 '特攻服の中学生ら200人 福岡',
 'オランダ 与党が第1党維持へ',
 'WBCで確率32万分の1の珍事',
 '中居の熱愛 事務所否定せず',
 '渡瀬さん 最強伝説と兄の存在']
```

BeautifulSoup4の要素選択メソッドには、
`find`,`find_all`もあるが、`select`を使うことが多い。

テキストの取得でnoneが返ってくる場合、
`.string`,`.text`,`.contents[0]`など試してみる
http://stackoverflow.com/questions/20750852/beautifulsoup-4-python-string-returns-none


## 天気予報を取得し、CSV/JSONとして保存

### 天気を取得

```python
from urllib.request import urlopen
from bs4 import BeautifulSoup
from pprint import pprint
import csv
import re

tenki = []
URL = "http://weather.livedoor.com/forecast/rss/area/130010.xml"

with urlopen(URL) as res:
  html = res.read().decode("utf-8")
  
soup = BeautifulSoup(html, "html.parser")
for item in soup.find_all("item"):
    title = item.find("title").string
    if title.find("[ PR ]") == -1: # ゴミ削除
        text = re.sub(r'\[?.+\]\s', '', title) # []内を削除
        result = text.split(' - ')
        tenki.append(result)
pprint(tenki)
```

```bash:出力結果
>>>
[['東京', '曇り', '最高気温℃', '7月23日(日)'],
 ['東京', '曇り', '最高気温32℃', '7月24日(月)'],
 ['東京', '曇り', '最高気温34℃', '7月25日(火)'],
 ['東京', '曇時々晴', '最高気温33℃', '7月26日(水)'],
 ['東京', '曇時々晴', '最高気温31℃', '7月27日(木)'],
 ['東京', '曇時々晴', '最高気温32℃', '7月28日(金)'],
 ['東京', '曇時々晴', '最高気温32℃', '7月29日(土)'],
 ['東京', '曇時々晴', '最高気温32℃', '7月30日(日)']]
```

基本html.parserにする
lxml.parserはたまにエラーがあるらしい
https://www.crummy.com/software/BeautifulSoup/bs4/doc/

### CSVとして保存

```python
with open('weather.csv','w',newline='') as f:
    writer = csv.writer(f)
    writer.writerow(['city','status','max','date'])
    writer.writerows(tenki)
```

```csv:weather.csv
place,status,max,date
東京,曇り,最高気温℃,7月23日(日)
東京,曇り,最高気温32℃,7月24日(月)
東京,曇り,最高気温34℃,7月25日(火)
東京,曇時々晴,最高気温33℃,7月26日(水)
東京,曇時々晴,最高気温31℃,7月27日(木)
東京,曇時々晴,最高気温32℃,7月28日(金)
東京,曇時々晴,最高気温32℃,7月29日(土)
東京,曇時々晴,最高気温32℃,7月30日(日)
```

### CSV->JSON

```python
rows = []
csvfile = open('weather.csv', 'r')
jsonfile = open('weather.json', 'w')
fieldnames = ('city','status','max','date')
reader = csv.DictReader(csvfile, fieldnames)
for index,row in enumerate(reader):
    if(index == 0): continue #  {"city": "city" ...} という表記は不要
    rows.append(row)
json.dump(rows, jsonfile, ensure_ascii=False, indent=2)
```

```json:weather.json
[
  {
    "date": "7\u670823\u65e5(\u65e5)",
    "status": "\u66c7\u308a",
    "city": "\u6771\u4eac",
    "max": "\u6700\u9ad8\u6c17\u6e29\u2103"
  },
  {
    "date": "7\u670824\u65e5(\u6708)",
    "status": "\u66c7\u308a",
    "city": "\u6771\u4eac",
    "max": "\u6700\u9ad8\u6c17\u6e2932\u2103"
  },
  ...
]
```


## ics.mediaの新着記事の画像を取得、ローカルに保存

```python
# ics.mediaをスクレイピング
import os
from urllib.request import urlopen
from bs4 import BeautifulSoup

URL = "https://ics.media/"
with urlopen(URL) as res:
    html = res.read().decode("utf-8")
    
soup = BeautifulSoup(html, "html.parser")

# 新着記事の画像（とタイトル）を取得
topics = soup.select(".topicsContainer")[0].nextSibling
topics_urls = topics.select(".thumb img")
topics_ttls = topics.select(".entryTitle a")
img_urls = [e["src"] for e in topics_urls]
img_ttls = [e.string for e in topics_ttls]

"""
# 相対パスの場合、絶対パスに変換
# リスト内包表記、三項演算子
img_urls = [u if u.find("http") == 0 else URL + u for u in img_urls]
"""

# 保存
img_dir = "images"
if not os.path.exists(img_dir):
    os.mkdir(img_dir)
    
for i,url in enumerate(img_urls):
    print("記事"+str(1+i), img_ttls[i])
    print(url)
    with urlopen(url) as res:
        img = res.read()
        with open(img_dir + "/entry_image%d.png" % (i+1), "wb") as f:
            f.write(img)
```

```bash:出力結果
>>>
記事1 ウェブ制作者なら意識してほしいCSS設計の基礎知識
https://ics.media/wp-content/uploads/2017/03/170315_eyecatch-640x256.jpg
記事2 CSS3だけで表現！ コピペで使えるマイクロインタラクション作りました
https://ics.media/wp-content/uploads/2017/03/main-640x256.png
記事3 広告系ウェブサイトのクオリティーが凄い! 最近話題になった国内サイト5選
https://ics.media/wp-content/uploads/2017/03/170227_web_trend_1611_1702_eye-640x256.jpg
・・・（略）・・・
```


兄弟要素の取得
http://tdoc.info/beautifulsoup/

osモジュール
https://docs.python.jp/3/library/os.html

enumerate関数 インデックスとともにループ
http://python.civic-apps.com/zip-enumerate/


## JavaScriptでDOMをレンダリングしているサイトから取得

chrome dev tool
Settings > Preferences > Debugger > Disable JavaScript

JSを無効にした上でリロードして要素が消えたら、
DOMがJSで動的に生成されているサイト

今回は[PhantomJS](http://phantomjs.org/)と[Selenium](http://selenium-python.readthedocs.io/api.html)を使う

`$ brew install phantomjs`
`$ pip3 install —upgrade selenium`

```python
from selenium import webdriver
from bs4 import BeautifulSoup
from pprint import pprint

URL = "https://dokusho-ojikan.jp/original/#!top"

driver = webdriver.PhantomJS()
driver.get(URL)
html = driver.page_source

bs = BeautifulSoup(html, "html.parser")
img_urls = [img.get("src") for img in bs.select("#unique-pickup img")]
for u in img_urls:
    print(u)
```

```bash:出力結果
>>>
https://cdn.om.dokusho-ojikan.jp/img/1f387c10-a8f8-11e6-8a10-525431b7cd60.jpg
https://cdn.om.dokusho-ojikan.jp/img/98c2e066-9a44-11e5-9ae2-52540e2de543.png
https://cdn.om.dokusho-ojikan.jp/img/7adbba1b-344b-11e5-be53-5254f877f35f.jpg
・・・（略）・・・
```