Beautiful Soup,Seleniumなどを使って企業名から会社情報を抜き出すコードを作成してみた

※初学者なので至らぬところが多々あります。大目に見てください
※様々な記事から引用した部分多数のキメラコードなので汚かったり無駄なところがあるかもしれませんがご容赦ください。コードが動いたので試しにこの記事を書いています。

### 初めに
日々生活をしていてこの会社って住所どこなんだろう…今回社ってどの業種何だろう…って気になり自動で調べてくれるコードを作ってみました。

概要としては
１．会社名の入ったCSVファイルから会社名を抜き出す
２．Seleniumで会社情報をまとめてくれているサイトを開き、会社名を検索する
（※今回は[BtoBプラットフォーム業界ch](https://b2b-ch.infomart.co.jp/company/search/top.page;JSESSIONID_B2BCH=8aeb61ff56260b5b600670677db3?0)というページで行いました
３．検索結果のサイトのテキストをMeCabで形態素解析し、今回は業種が書いてある場所を抽出する
４．抽出した業種情報をCSVファイルに書き出す

となります。ウェブスクレイピングと形態素解析（といってもライブラリが全部やってくれるんですけど）を含んだものになります。selenium.~~~系はseleniumさえ入ってれば使えます。MeCabの辞書であるipadicのダウンロードの仕方は調べたほうがよいかもしれません（どのサイトを参照したか忘れました；；）

### ライブラリ
今回使うライブラリは以下の通りです
```python
import requests
from bs4 import BeautifulSoup
import csv
import MeCab
import ipadic #MeCabの辞書
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
```
requestsはhttpリクエストを、BeautifulSoupはサイトのHTML取得など、MeCabは形態素解析、seleniumはブラウザの操作やテキストの取得をやってくれます。

### コードの解説
最初に全部を乗っけてしまうと
```python
import requests
from bs4 import BeautifulSoup
import csv
import MeCab
import ipadic #MeCabの辞書
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# CSVファイルを開く
count=0 #何個会社名を読み取ったかの変数
company=[] #CSVから読み取った会社名を入れる
with open('companyname.csv', newline='', encoding='utf-8') as csvfile: #CSVを開く
    reader = csv.reader(csvfile)
    # 各行のデータを読み込む
    for row in reader:
        print(row)
        count+=1
        #companyリストにcsvファイルの単語を格納
        company.append(row)
# companyリストを一つずつ参照
for i in range(0,1): #末尾はcount+1にしてください
    address=[] #会社の情報を入れるためのリスト
    search_word = f'{company[i]}'
    # 上位から何件までのサイトを抽出するか指定する
    print(f'【検索ワード】{search_word}')
    driver = webdriver.Chrome() #Chromの起動

    driver.get('https://b2b-ch.infomart.co.jp/company/search/top.page?1794') #URLのページを開く、ここではBtoB検索ページ
    driver.maximize_window() #Window最大化
    form = driver.find_element(By.XPATH, '//*[@id="tfKeyWord"]') #XPathから検索ボックスを探す
    form.send_keys(search_word) #検索ボックスに言葉を入れる
    form.send_keys(Keys.RETURN) #Enterキーをページ上で押す
    
    try: #今回のページでは検索ワードがなかった時、下の行でエラーになるため例外処理、これはページごとに異なる
        element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, '//*[@id="form"]/div[3]/div/div/div[3]/div/p[1]')))#ページが完全にロードされるのを待ってページ上のXpathの場所を取得。この場所はページごとに異なる企業名がなかった時の「見つかりませんでした」の部分
    
        text_NoResult = element.text
    
        if text_NoResult == "企業が見つかりませんでした。":#上のXPATHの文言を入れる。このとき、Noresultと書き込むようにしている。
            address=["NoResult"]
            print(address)
            with open('gyousyukekka.csv', 'a',encoding='utf-8') as f:
                writer = csv.writer(f)
                writer.writerow([search_word,address])
                driver.close()
            continue
    except:
        first_result = driver.find_element(By.XPATH,'//*[@id="lnkCompanyName"]')  # 企業がヒットした時、その企業ページに行くためにURLの場所をXPATHで指定
        first_result.click()#その箇所をクリック

        url = driver.current_url

        driver.close()

        # Chromeから接続をリクエスト
        request = requests.get(url)
        # Googleのページ解析を行う
        soup = BeautifulSoup(request.text, "html.parser")
        search_site_list = soup.select('div.kCrYT > a')

        def extract_text_from_url(url):
            # URLからHTMLを取得
            response = requests.get(url)
            html_content = response.content.decode("utf-8", "ignore")
            # BeautifulSoupを使用してHTMLを解析
            soup = BeautifulSoup(html_content, "html.parser")
            # 記事の本文を取得
            for script in soup(["script", "style"]):
                script.decompose()
            # テキストの抽出
            text=soup.get_text()
            lines= [line.strip() for line in text.splitlines()]
            text="\n".join(line for line in lines if line)
            return text
        
        text = extract_text_from_url(url)
        
        # MeCabオブジェクトの生成
        m = MeCab.Tagger(ipadic.MECAB_ARGS)

        # 形態素解析の実行
        result = m.parse(text)

        wordarray=[] #MeCabで得た結果を入れるためのリスト
        node = m.parseToNode(text) #nodeを使うとMeCabで１語ずつ取り込めるらしい
        while node:
            word = node.surface
            wordarray.append(word)
            node = node.next
        address=[]
        for i in range(1,len(wordarray)):
            if wordarray[i] == "業種": #ページのMeCabの情報からほしい会社情報がどの単語の後にあって前にあるのか目視で確認し、条件に入れる。今回は業種情報が業種の後、表示の前にある
                for j in range(i,len(wordarray)):
                    if wordarray[j] == "表示":
                        break
                    else: address.append(wordarray[j])
        print(address)

    with open('gyousyukekka.csv', 'a',encoding='utf-8') as f:
        writer = csv.writer(f)
        if not address:
            writer.writerow("ヒットなし")
        else: 
            writer.writerow([search_word,address])
    address = [] #リセット



```
こんな感じになります。
コメントアウトで解説を入れてはいますが詳しく見ていきましょう。

### １．会社名の入ったCSVファイルから会社名を抜き出す
```python
# CSVファイルを開く
count=0 #何個会社名を読み取ったかの変数
company=[] #CSVから読み取った会社名を入れる
with open('companyname.csv', newline='', encoding='utf-8') as csvfile: #CSVを開く
    reader = csv.reader(csvfile)
    # 各行のデータを読み込む
    for row in reader:
        print(row)
        count+=1
        #companyリストにcsvファイルの単語を格納
        company.append(row)
```
まずはCSVを読み込むところから行きましょう。
`with open`でCSVファイルを開きます。今はcompanyname.csvの各行に会社名が入っています。各行にある会社名をfor文で読み込み、companyリストに入れていきます。

### ２．Seleniumで会社情報をまとめてくれているサイトを開き、会社名を検索する
```python
for i in range(0,1): #末尾はcount+1にしてください
    address=[] #会社の情報を入れるためのリスト
    search_word = f'{company[i]}'
    # 上位から何件までのサイトを抽出するか指定する
    print(f'【検索ワード】{search_word}')
    driver = webdriver.Chrome() #Chromの起動

    driver.get('https://b2b-ch.infomart.co.jp/company/search/top.page?1794') #URLのページを開く、ここではBtoB検索ページ
    driver.maximize_window() #Window最大化
    form = driver.find_element(By.XPATH, '//*[@id="tfKeyWord"]') #XPathから検索ボックスを探す
    form.send_keys(search_word) #検索ボックスに言葉を入れる
    form.send_keys(Keys.RETURN) #Enterキーをページ上で押す
    
    try: #今回のページでは検索ワードがなかった時、下の行でエラーになるため例外処理、これはページごとに異なる
        element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, '//*[@id="form"]/div[3]/div/div/div[3]/div/p[1]')))#ページが完全にロードされるのを待ってページ上のXpathの場所を取得。この場所はページごとに異なる企業名がなかった時の「見つかりませんでした」の部分
    
        text_NoResult = element.text
    
        if text_NoResult == "企業が見つかりませんでした。":#上のXPATHの文言を入れる。このとき、Noresultと書き込むようにしている。
            address=["NoResult"]
            print(address)
            with open('gyousyukekka.csv', 'a',encoding='utf-8') as f:
                writer = csv.writer(f)
                writer.writerow([search_word,address])
                driver.close()
            continue
    except:
        first_result = driver.find_element(By.XPATH,'//*[@id="lnkCompanyName"]')  # 企業がヒットした時、その企業ページに行くためにURLの場所をXPATHで指定
        first_result.click()#その箇所をクリック

        url = driver.current_url

        driver.close()

        # Chromeから接続をリクエスト
        request = requests.get(url)
        # Googleのページ解析を行う
        soup = BeautifulSoup(request.text, "html.parser")
        search_site_list = soup.select('div.kCrYT > a')

        def extract_text_from_url(url):
            # URLからHTMLを取得
            response = requests.get(url)
            html_content = response.content.decode("utf-8", "ignore")
            # BeautifulSoupを使用してHTMLを解析
            soup = BeautifulSoup(html_content, "html.parser")
            # 記事の本文を取得
            for script in soup(["script", "style"]):
                script.decompose()
            # テキストの抽出
            text=soup.get_text()
            lines= [line.strip() for line in text.splitlines()]
            text="\n".join(line for line in lines if line)
            return text
        
        text = extract_text_from_url(url)
```
ここは長いですね。
companyリストに入っている会社名をfor文で１社ずつ参照していきます。
だいたいはコメントアウトに書いてある通りなのですが、重要なのは`form = driver.find_element(By.XPATH, '//*[@id="tfKeyWord"]')`や
`first_result = driver.find_element(By.XPATH,'//*[@id="lnkCompanyName"]')`など要所要所をXPATHといわれるパスで参照しています。詳しい仕組みはわかりませんが、HTMLで参照してもいいけれども、詳しい場所まで参照するのはできない/面倒だったりするので、XPATHで楽に指定するのがいいみたいです。
XPATHを取得するには実際のHPでF12を押し、右に出てきた画面で![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3792007/5f1bbf20-07b8-5e46-5a3d-35d896b8dd44.png)マークから場所を指定して右クリック→コピー→XPATHから取得できます。
HPごとに検索ボックスの場所が違うと思うので都度XPATHを取得してください。

さて、コードの説明に戻りますと、Seleniumに含まれている関数で開いたHPの検索ボックスに会社名を記入し、エンターを押します。
try except文のところは会社名がヒットしなかったときの対処を記しています。
今回のページでは会社名がヒットしないとき、”企業が見つかりませんでした”とでるので、その場所のXPATHを指定し、テキストを取得したのちif文でNoresultとCSVに書き込むよう分岐させています。
exceptでは企業名がひっとしたときにヒットしたサイトを開くためにclickで開き、request,BeautifulSoupをつかってHTMLおよびテキストの取得をしています。ここはほかの記事を参考にしたので、今回のコード用に最適化してないかもしれません。（ごめんなさい）

今回はtry except文で例外処理をしていますが、サイトによっては企業名がヒットしないとき、XPATHを指定した場所からテキストを抽出するときにエラーをはかないサイトもあるのでその時はif文での条件分岐だけでよかったりもしています。ページに合わせて変える必要があるのはちょっと手間ですね…

なにはともあれ調べたい企業の業種が乗っているページのURL、テキストを取得することができました。

### ３．検索結果のサイトのテキストをMeCabで形態素解析し、今回は業種が書いてある場所を抽出する
```python
 m = MeCab.Tagger(ipadic.MECAB_ARGS)

        # 形態素解析の実行
        result = m.parse(text)

        wordarray=[] #MeCabで得た結果を入れるためのリスト
        node = m.parseToNode(text) #nodeを使うとMeCabで１語ずつ取り込めるらしい
        while node:
            word = node.surface
            wordarray.append(word)
            node = node.next
        address=[]
```
MeCabを使うと言葉の羅列を単語ごとに区切ってくれます。例えば、
私はパンが好きです → 私、は、パン、が、好き、です
のように形態素解析してくれます。今回はこれを使って”業界”という言葉をみつけてそのあとの言葉を取り出そう！としてます。

コードの解説をするとMeCabの関数をこのように使うと分割してくれた言葉を配列に取り組めるらしいです。（MeCabすごい）

### ４．抽出した業種情報をCSVファイルに書き出す
```python
for i in range(1,len(wordarray)):
            if wordarray[i] == "業種": #ページのMeCabの情報からほしい会社情報がどの単語の後にあって前にあるのか目視で確認し、条件に入れる。今回は業種情報が業種の後、表示の前にある
                for j in range(i,len(wordarray)):
                    if wordarray[j] == "表示":
                        break
                    else: address.append(wordarray[j])
        print(address)

    with open('gyousyukekka.csv', 'a',encoding='utf-8') as f:
        writer = csv.writer(f)
        if not address:
            writer.writerow("ヒットなし")
        else: 
            writer.writerow([search_word,address])
    address = [] #リセット
```
テキストの解析が終わったので、業種の部分を取り出しましょう。
かなり力技でスマートではないのですが、今回のページでは必ず”業種”のあとに業種が書いてあり、その後に”表示”という言葉が来ます。ということはfor文でこれらの言葉を探し出しその間にある言葉が欲しかった業種となります。それを結果を入れるCSVに書き込めば終わりです！

### 実際に動かすと
会社名を入れるCSVとして
```CSV
Python難しい
Qiita株式会社
```
を用意し、コードを回すと、Python難しいについては![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3792007/24a95b63-3189-8ba3-44c2-f40bbb214ea9.png)
となり、企業が見つかりませんでしたという部分が参照できるのでtry分のところが作動します。
Qiita株式会社については見事ヒットし、![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3792007/909edd85-03c4-6259-1e2a-76d0fc4fab48.png)
のように企業情報が載っているページが開けました。ただ業種が書いていないので値は得られませんでしたね
結果のCSVは
```
['Python難しい'],['NoResult']

['Qiita株式会社'],['業種']
```
のように出力されました。

ちゃんと業種が得られる例については皆さんでやってみてください。

### 最後に
今回のコードはとりあえず動けばいいやといった感じで適当に書いてあります。無駄があってもご了承ください。
文章もだいぶ拙いと思うのですが読んでいただきありがとうございました。
誰かの助けになれば幸いです。