Pythonでファッション画像とテキスト文を【スクレイピング】してみた。

#はじめに
Qiita初投稿なのでお手柔らかに。

現在修士1年で、研究でディープラーニングとファッションをテーマに何かやろうと考えています。

そこでまず、勉強がてら画像を分類してみようと思い、自分でデータを集めようと今に至るわけです。（既存のデータセット使って分類すればええやん。。。）

#実行環境
- Python3.7.4
- MacOS Mojave10.14.6

#画像と商品紹介文をセットでスクレイピングしてみた。
今回はPythonのlxmlライブラリを使って画像とテキスト文をスクレイピングしました。スクレイピングどころかプログラミング自体初心者だったので、技術評論社が出版している[「Python クローリング&スクレイピング」](https://gihyo.jp/book/2019/978-4-297-10738-3)を参考にしながら自分でコードを組みました。この時からQiitaの存在を知っていれば、Qiitaで解決したはず、、、。コードは以下になります。

####補足
今回はメンズのTシャツのみをスクレイピングしたかったためカテゴリーと性別を指定しました。ZOZOTOWNのECサイトは各ページに135の商品があり、それぞれの商品をクリックするとその商品の詳細ページにリンクが移ります。
今回は移った先のページのテキストとトップ画像を一枚スクレイピングするコードを書いています。

```python:scraping_zozo_img_text.py
from typing import Iterator
from typing import List
import requests
import lxml.html
import time
import csv
import os
```
以下がメインの実行になります。

```python
csvlist = [['no', 'URL', 'item_text']]

i = 0
u = 0
j = 0
URL = "https://zozo.jp/men-category/tops/tshirt-cutsew/?pno="
for page in range(1, 100):
    time.sleep(1)
    pageUrl = "https://zozo.jp/men-category/tops/tshirt-cutsew/?pno=" + str(page)
    response = requests.get(pageUrl)

    #一覧ページにある各アイテムの詳細ページのURLを取得↓↓↓↓
    urls = scrape_item_page(response) #各アイテム(一覧ページの画像と対応)の詳細ページへのURLが取得される。


    for url in urls:
        j = j + 1
        time.sleep(1)

        #画像を拾ってフォルダに保存↓↓↓↓↓↓↓
        img_url = get_image(url)
        w_img = requests.get(img_url)
        with open(str('picture_zozo/')+str(j)+str('.jpg'),'wb') as file:
            file.write(w_img.content)

        info = scrape_item_infomation(url)
        print(info)

        csvlist.append([j, url, info])


f = open("item_text.csv", 'w')
writecsv = csv.writer(f)

writecsv.writerows(csvlist)

f.close()


```
10行目のresponseにはURLのページ内のさまざまな情報が格納されているイメージを持ってください。


##定義した関数の詳細

###各ページのアイテムごとのURLを取得する関数

```python

def scrape_item_page(response: requests.Response) -> Iterator[str]:
    html = lxml.html.fromstring(response.text)
    html.make_links_absolute(response.url)

    url=[]
    for a in html.cssselect('#searchResultList > li > div[class="catalog-item-container"] > a'):
        url.append(a.get('href'))

    return url
```
メインの実行の12行目。
response.textはhtmlのコード全文になります。fromstring関数を使うことで、直接HtmlElementが得られます。
make_links_absoluteで相対リンクから絶対リンクに書き換えます。
6行目でcssselectを使ってhtmlのタグを辿り各商品の詳細ページのURLが含まれるタグの情報を取得します。
7行目で取得したタグの中の**href**に続くURLを取得できます。
（各ページ内で135個ずつURLを取得します。）

###各アイテムの詳細ページ中のテキスト文を取得する関数

```python
#各アイテムのURLにアクセスして、商品紹介文を取得する関数を定義

def scrape_item_infomation(url):
    response = requests.get(url)
    response.encoding = response.apparent_encoding
    html = lxml.html.fromstring(response.text)
    infomation = html.cssselect('#tabItemInfo > div[class="innerBox"] > div[class="contbox"]')
    info = infomation[0].text_content()

    return info
```
メインの15行目で135個から１つずつURLを選択し、画像とテキストを獲得します。
5行目のencodingで文字化けを防ぎます。
以下は先ほどと同じで、最後に**info**にテキスト文を代入して、返します。


###各アイテムの画像情報（画像URL）を取得する関数
```python
#画像の情報をさらってくる関数を定義

def get_image(url): #一覧ページのURL
    response = requests.get(url)
    html = lxml.html.fromstring(response.text)
    html.make_links_absolute(response.url)
    image = html.cssselect('#photoMain > img')
    for img in image:
        img_url = img.get('src')
        print(img_url)

    return img_url

```
テキストを取得する流れとほとんど変わりません。
ここで、画像の情報が手に入ります。

メインの21行目でフォルダに画像を保存するコードを書いています。
この時、スクリプトとフォルダが同じ階層にある必要があります。
空のフォルダは事前に作成しておかないと、エラーが出るので注意してください。

ちなみに僕は'picture_zozo'というフォルダを作りました。

最後にCSVファイルに「何個めのアイテムであるか(ナンバー)」、「そのアイテムのURL」、「テキスト文」を保存します。取得した画像とURLをクリックした時のページにある画像が同一商品であるかどうかの確認に使えます。

ちなみに画像はこんな感じに保存されます。
<img width="555" alt="スクリーンショット 2020-05-22 0.23.19.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/641214/f3875a96-44f6-4c6b-ea84-dd110e8fbdb1.png">

ちなみにCSVファイルはこんな感じです。

| No | URL | text |
|:-----------:|:------------:|:------------:|
| 1       | https://〜        | 〇〇         |
| 2     | https://〜    | △△      |
| 3       | https://〜       | □□         |

Noの値と○.jpgの値が同じになるようになってます。

メインの実行のfor文以下で、同じURLを用いているのでテキストと画像の組み合わせは同一の商品のものになっています。

#まとめ
初めてQiitaの投稿を試みましたが、文章で伝えるのはなかなか難しいですね。僕自身も完璧に説明できるほど習得している訳ではないので説明がわかりずらい部分もあると思いますが、ご容赦ください。イメージが湧きにくい場合は、実際にコードを参考にして、print()を用いて確認してみるといいと思います。