Pythonを用いたScraping入門(BeautifulSoup少し使用)

こんにちは！
先日プログラミングスクールを卒業しまして、10月半ばから受託の会社で働き始めた24歳！（拳で）

今回業務上でpythonを用いたscrapingをはじめて実装しまして、
まぁ時間が経つと忘れそうなので備忘録〜・・・

今回の学びをバネにもっと高みへいくぜ！！・・・・ということで


```今回作ったscrapingAppの仕様について.py

Global_urls=[]
Global_log=[]

function（url）
    #Global_logにurlを代入
    パート①
      #そのurlのhtmlテキストをget
      #htmlテキストの中からaタグ群だけget
      #aタグ群からhrefパラメータ群を抽出
      #hrefパラメータ群の一律的な整形
      #hrefパラメータ群をGlobal_urlsに代入
      #Global_urlsに重複があれば重複削除
    パート②
      #for文でGlobal_urlsの中身を順番に取り出す（変数urlとして取り出す）
      #Global_log内に当該urlがあればskip（functionにおいて処理済のurlがlogに貯まるので）
      #functionを当該urlを引数に呼び出す（再帰関数）

#ここまででtopページのurlから、そのurlと関わりのある全てのurlを、Global_urlsに格納することができます
#これをファイルに書き出すなり、printするなりと好き放題できるようになります。

```

```使ったモジュール.py
1: requests
  #urlに対してrequestを送ることができる
  #response= requests.get(url)→getアクションで送ってる
  #response.textとすることでhtmlのテキスト情報が取れる
  #response.status_codeと書くことでurlの有効性を調べることができる

2: lxml
  #requestsのreturnをBeautifulSoupオブジェクトに変換する際に使用
  #parserの一種（？）（要確認）

3: BeautifulSoup
  #requestsのreturn(htmlのテキスト情報)から特定のタグを指定して収集・list化してくれる
  #つまりhtmlのテキストの中からaタグのものだけ収集！list(配列)に順番に突っ込む！ということ出来る
  #思ったより使いどころが少なかったので、使い方下手なのかも・・・

4: re
  #pythonで正規表現を使う際に必要

5: urllib.parse
  #urlをparseするのに必要
  #https://qiita.com/drafts/4f6f4e3ad2d5048ced4f/edit
  #上記を[scheme="https", netloc="qiita.com", path="/drafts/4f6f4e3ad2d5048ced4f/edit"]みたいに解析してくれる
  #return値.pathとかってやると path="/drafts/4f6f4e3ad2d5048ced4f/edit"これだけ取れてくる便利

7: urllib.request
  #requests.status_codeとほぼ同じ使い方、こちらは404がreturnで返るというより404の場合エラーが起きる
  #response.status_codeに気づく前の名残。当時は例外処理で404エラーが出ればskipみたいな使い方をしていた

8: pdb
  #デバッガー。ブレークポイント。　railsで言う所のbinding.pry　laravelで言う所のeval(\Psy\sh())

9: sys
  #再帰関数の再帰回数の上限を操作できる関数が入ってる
  #ちなみにsys.setrecursionlimit(10000)これ

10: time
11: datetime
  #10-11はまとめて紹介。datetime.datetime.fromtimestamp(time.time())
  #こう書くと現在の時間が取れる。詳細はよく調べてないので割愛

12: pathlib
  #path操作の為のモジュール。path内の拡張子が取りたかっただけなので使い方はparser的
  #詳細はよく調べてないので割愛

13: unicodedata
  #文字コードを変換してくれるモジュール。
  #クエリ文字列に混じってる日本語に対してasciiじゃ対応できないよって言われたので組み込むことに

```

##メモリ無視してわかりやすさ重視で書いていくYO
qiita上では変数めっちゃ使うってことです。
実際は変数少なめにコード書いてます。

```パート1.py
Global_urls=[]
Global_log=[]

# ターゲットURL
# url= 'https://qiita.com'
def getLinksFromALink(url)
  # このurlは処理したことがあるよって記録を残したいのでlogに追加
  Global_log.append(url)
  #urlへrequest送って、返ってきたresponse
  response= requrst.get(url)

  # urlのhtmlテキストをget
  htmlText= response.text

  # htmlテキストをBeautifulSoupのオブジェクトへ変換
  SoupObject= BeatutifulSoup('htmlText', 'lxml')

  # BeautifulSoup化したhtmlテキストの中からaタグ群だけget
  aTags= SoupObject.findAll('a')

  # aタグ群からhrefパラメータ群を抽出
  # 抽出データをglobal_urlsへ
  # ちなみにここで出てくるlinkは相対パスだったり、絶対パスだったり、.htmlという拡張子がついていたり、、、
  # はたまたリンク切れのurlが紛れていたり、クエリ文字列が違うだけで同じlinkが何個も入ってきたり、、、etc
  # と、色んな値が取れるのでこのfor文の真ん中らへんで別途作成した条件分岐及びlink再構成の関数を挟むとgood
  #またドメインを指定する際もここで条件を挟むのがgood
  for aTag in aTags:
    link= aTag.get('href')
    Global_urls.append(link)

  #Global_urlsに重複があれば重複削除
  # list内の重複削除をしてくれるdict.fromkeys、結果dict型になってしまうのでlist()で再びlist型に
  Global_urls= list(dict.fromkeys(Global_urls))

```

```パート２.py

  for url in Global_urls:
    #もし今回取り出されたurlと同じ値がlogにあればskip
    if url in Global_log:
      continue 
    # そうでなければもう一度この関数を呼び出す
    # 再帰的に各urlと関わりのあるurlsをGlobal_urlsに格納し続ける
    # 順次増え続けるGlobal_urlsの全てにアクセスし終えるまで関数は終わらない
    getLinksFromALink(url)
  
  #全てが終わればGlobal_urlsをreturnする
  return Global_urls

  # あとはこのurls達をファイルに書き出したり、printしたりする
  # 自分はファイルに書き込んだので後日それもまとめる

```

##改めて

だいぶ端折ってますが、大枠で言えばこんな感じの関数を作ってました。
この後のファイル作成とか、遭遇したエラーとかについては来週中にまとめようと思いますー

```function.py
Global_urls=[]
Global_log=[]

def getLinksFromALink(url)
  Global_log.append(url)
  response= requrst.get(url)

  htmlText= response.text
  SoupObject= BeatutifulSoup('htmlText', 'lxml')
  aTags= SoupObject.findAll('a')

  for aTag in aTags:
    link= aTag.get('href')
    Global_urls.append(link)
  Global_urls= list(dict.fromkeys(Global_urls))

  for url in Global_urls:
    if url in Global_log:
      continue 
    getLinksFromALink(url)
  
  return Global_urls

```