seleniumでいろいろAPIモドキ


seleniumを使って、httpリクエスト上げればいろいろ情報を拾ってくる機能を作って遊ぶ
かなり昔に書いたものですが、ほとんど修正無しで長期間動いているもの（=参照先Webページの仕様変更が少なく安定しているもの）を投稿。

適当なwebサーバ(CGI有効化)を用意して、cgi-bin以下にseleniumを使うスクリプトを配置する。

``` python
#!/usr/bin/python3.9
# -*- coding: utf-8 -*-
import os, http.server, socketserver

PORT = 80
os.chdir('/app')

class handler(http.server.CGIHTTPRequestHandler):
   cgi_directories = ["/cgi-bin"]
httpd = socketserver.TCPServer(("0.0.0.0", PORT), handler)
httpd.server_name = "webscraping"
httpd.server_port = PORT
httpd.serve_forever()

```

■■google翻訳から翻訳結果を生成
ちょっとした1行文章翻訳ならapi使わなくても、これで十分

``` python

#!/usr/bin/python3.9
# -*- coding: utf-8 -*-
import cgi, socket
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
import re, sys, time

params = cgi.FieldStorage()
print('Content-Type: text/html; charset=UTF-8\n')

search_string = params['req'].value.replace("\n","。").replace("\t","").strip()
type = params['type'].value

if type == "J2E":
  url='https://translate.google.co.jp/?hl=ja&sl=ja&tl=en&text=私の猫%0A'+search_string+'&op=translate'
  word_t="My cat"
else:
  url='https://translate.google.co.jp/?hl=ja&sl=en&tl=ja&text=My cat%0A'+search_string+'&op=translate'
  word_t="私の猫"

## メモ
# google翻訳ページのclass/id/nameは仕様変更でよく変わるため、翻訳結果をfind_elementsでそのまま探すのは困難。
# そこで1行目に「私の猫」、2行目に翻訳したい文字列で翻訳させて、1行目の翻訳結果「My cat」を探す。
# 翻訳結果は1行ごとに異なるspanに保管されているが、以下のような構造になっている
# 親span
#   |- ①子span - 子span(1行目の翻訳結果)
#   |- ②子span - いろいろ(1行目の翻訳に対する追加情報)
#   |- ③子span - 子span(2行目の翻訳結果)
#   |- ④子span - いろいろ(2行目の翻訳に対する追加情報)
#
# このうち、①と③は同じCLASS_NAMEが入る規則っぽいので、①にある「My cat」から親spanと子spanを両方探し、
# 親spanの配下から、①のCLASS_NAMEを探し、その2番めの要素を探せば「2行目の翻訳結果」を探せる。


service = Service(executable_path=ChromeDriverManager().install())
options = Options()
options.binary_location = "/usr/bin/google-chrome"
options.add_argument('--headless')
options.add_argument("--no-sandbox")
options.add_argument('--disable-dev-shm-usage')
driver = webdriver.Chrome(service=service, chrome_options=options)

driver.get(url)
print("title : " + driver.title,file=sys.stderr)
span_p=""
for i in range(50):
  try:
    time.sleep(0.2)
    span_p = driver.find_element(By.XPATH,'//span/span[span[text()="'+word_t+'"]]')
  except: pass
  else: break
if span_p!="":
  span_pp = driver.find_element(By.XPATH,'//span[span/span[text()="'+word_t+'"]]')
  targetclass = span_p.get_attribute("class")
  print("span_p class=" + targetclass + "    span_pp class=" + span_pp.get_attribute("class"),file=sys.stderr)
  # アップデートでBy.CLASS_NAMEが半角スペースを含むCLASS_NAMEを探せなくなったのでBy.CSS_SELECTORに変更
  try:
    output=""
    for i in span_pp.find_elements(By.CSS_SELECTOR,"."+targetclass.replace(" ","."))[1:]: output+=i.text
  except:
    output = "翻訳エラー"
else: outut = "翻訳エラー"

print(output)

```
上記をcgi-bin/get_translate.pyにでも置いて、以下で英語翻訳結果が帰る
http://IPアドレス/cgi-bin/get_translate.py?type=J2E&翻訳したい日本語
以下で日本語翻訳結果が帰る
http://IPアドレス/cgi-bin/get_translate.py?type=E2J&req=翻訳したい英語


■■入力した単語のwikipediaページをgoogleで検索し、Wikipediaの概要部分をもってくる。

``` python
#!/usr/bin/python3.9
# -*- coding: utf-8 -*-
import cgi, socket
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from bs4 import BeautifulSoup
import re, sys

params = cgi.FieldStorage()
print('Content-Type: text/html; charset=UTF-8\n')

search_string = params['req'].value.replace("\n","。").replace("\t","").replace("とは？","").strip() +'とは？'

service = Service(executable_path=ChromeDriverManager().install())
options = Options()
options.binary_location = "/usr/bin/google-chrome"
options.add_argument('--headless')
options.add_argument("--no-sandbox")
options.add_argument('--disable-dev-shm-usage')
driver = webdriver.Chrome(service=service, chrome_options=options)

driver.get("https://www.google.com/search?q="+search_string+"+site:ja.wikipedia.org")
print("title : " + driver.title,file=sys.stderr)

try: wikiurl = driver.find_element(By.PARTIAL_LINK_TEXT, "Wikipedia").get_attribute("href")
except: print("ごめんなさい。わかりません・・・・")

print("wikiurl : " + wikiurl,file=sys.stderr)

try: driver.get(wikiurl)
except: print("ごめんなさい！わかりません！")

print("title : " + driver.title,file=sys.stderr)

soup = BeautifulSoup(driver.page_source, "lxml")
infos = soup.findAll("div", class_="mw-parser-output")
for info in infos:
  for s in info(['script', 'style','table' ,'div','span']): s.decompose()
  # 本文最初の概要部分（class等の無い最初の<p>）を取得し、リンクや修飾タグなどを除去、さらに括弧補足も削除
  # 長過ぎる説明は、30文字目以降の最初の「。」で終わる
  for i in info.find_all("p")[:3]:
   if str(i).startswith("<p>"):
     data = ''.join((i).stripped_strings)
     data = re.sub(r'（.*?）','',data)
     data = re.sub(r'\[.*?\]','',data)
     results = data.split("。")
     #print(results)
     result = ""
     for i in results[:-1]: #最後の1つは空文字なので除外
       result += i+"。"
       if len(result)>30:break
     if len(result)>5:
       print(result)
       break

if len(result)<6: print("すまねぇ。わからん。")
``` 

使い方は先と同じ。
cgi-bin/get_wikipedia.pyにでも保存して、以下で情報取得
http://IPアドレス/cgi-bin/get_wikipedia.py?req=猫

→ネコは、狭義には食肉目ネコ科ネコ属に分類されるリビアヤマネコが家畜化されたイエネコに対する通称である。

■■googleニュースの一番上の記事のタイトルを取得

``` python
#!/usr/bin/python3.9
# -*- coding: utf-8 -*-
import cgi, socket
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
import re, sys

params = cgi.FieldStorage()
print('Content-Type: text/html; charset=UTF-8\n')

search_string = params['req'].value.replace("\n","。").replace("\t","").strip()

service = Service(executable_path=ChromeDriverManager().install())
options = Options()
options.binary_location = "/usr/bin/google-chrome"
options.add_argument('--headless')
options.add_argument("--no-sandbox")
options.add_argument('--disable-dev-shm-usage')
driver = webdriver.Chrome(service=service, chrome_options=options)

driver.get("https://news.google.com/search?q="+search_string+"&hl=ja&gl=JP&ceid=JP:ja")
print("title : " + driver.title,file=sys.stderr)

try: links=driver.find_elements(By.XPATH,f'//a[contains(@href,"./articles/")]')
except: print("ごめんなさい。わかりません・・・・")

NewsNotFind=True
for i in links:
  if i.text != "":
    print (i.text)
    NotFound=False
    break

if NewsNotFind: print("ごめんなさい。見つかりません")
```

使い方は同じ。
cgi-bin/get_googlenews.pyにでも保存して、以下で情報取得
http://IPアドレス/cgi-bin/get_googlenews.py?req=Linux

→Xbox Cloud GamingをLinux環境でプレイしたら画質が低下したという報告


仕様の変化が激しいサービスの場合はおとなしく有償のAPIを使った方が良いが、上に上げたようなものは、2年近く経過しても使えているので寿命は長そう。

■上記環境を動かしているDockerfile。
バージョン間互換の厳しい機能は使っていないので、適当な環境でも動くとは思いますが一応。
seleniumで他にも色々遊んだ環境なので余計な設定も含まれているかも。
chromedriverはCOPYで配置しているので、予め適当なところから入手しておいてください。
かなり昔に作ったものなので、ubuntuやpythonのバージョンは古いものに変えても動くはず。

``` Dockerfile
FROM ubuntu:20.04
USER root

ENV PYTHONUNBUFFERED True
ENV LANG C.UTF-8
ENV LANGUAGE en_US:
COPY ./chromedriver /usr/bin/chromedriver
RUN apt-get update
RUN apt-get install -y wget curl gnupg tzdata vim
RUN sh -c 'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list'
RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
RUN apt update
RUN apt install -y google-chrome-stable software-properties-common
RUN add-apt-repository -y ppa:deadsnakes/ppa
RUN apt install -y python3.9 python3-distutils
RUN curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
RUN python3.9 get-pip.py
RUN pip3.9 install lxml selenium webdriver_manager cssselect bs4
RUN chmod 777 /root

RUN cp /etc/localtime /etc/localtime.org
RUN ln -sf  /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

COPY app /app
CMD exec /app/app.py
```