単純なWebスクレーパーを開発しようとしています。 HTMLコードなしでテキストを抽出したい。実際、私はこの目標を達成していますが、JavaScriptがロードされている一部のページでは、良い結果が得られないことがわかりました。
たとえば、JavaScriptコードがテキストを追加する場合、それを見ることができません。
response = urllib2.urlopen(request)
追加されたものなしで元のテキストを取得します(クライアントでJavaScriptが実行されるため)。
したがって、私はこの問題を解決するためのアイデアを探しています。
編集2017年12月30日:この回答はGoogle検索の上位の結果に表示されるため、更新することにしました。古い答えはまだ終わりです。
dryscapeはもうメンテナンスされておらず、dryscape開発者が推奨するライブラリはPython 2のみです。 SeleniumのpythonライブラリーをPhantom JSとともにWebドライバーとして使用するのは、作業を完了するのに十分かつ簡単です。
Phantom JS をインストールしたら、phantomjs
バイナリが現在のパスで利用可能であることを確認します。
phantomjs --version
# result:
2.1.1
例として、次のHTMLコードでサンプルページを作成しました。 ( リンク ):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Javascript scraping test</title>
</head>
<body>
<p id='intro-text'>No javascript support</p>
<script>
document.getElementById('intro-text').innerHTML = 'Yay! Supports javascript';
</script>
</body>
</html>
javascriptを使用しない場合:No javascript support
およびjavascriptを使用する場合:Yay! Supports javascript
import requests
from bs4 import BeautifulSoup
response = requests.get(my_url)
soup = BeautifulSoup(response.text)
soup.find(id="intro-text")
# Result:
<p id="intro-text">No javascript support</p>
from Selenium import webdriver
driver = webdriver.PhantomJS()
driver.get(my_url)
p_element = driver.find_element_by_id(id_='intro-text')
print(p_element.text)
# result:
'Yay! Supports javascript'
Pythonライブラリ dryscrape を使用して、JavaScript駆動のWebサイトをスクレイピングすることもできます。
import dryscrape
from bs4 import BeautifulSoup
session = dryscrape.Session()
session.visit(my_url)
response = session.body()
soup = BeautifulSoup(response)
soup.find(id="intro-text")
# Result:
<p id="intro-text">Yay! Supports javascript</p>
JavaScriptで生成されたコンテンツはすべてDOMでレンダリングする必要があるため、正しい結果が得られません。 HTMLページを取得するとき、JavaScriptによって変更されていない初期のDOMを取得します。
したがって、ページをクロールする前にJavaScriptコンテンツをレンダリングする必要があります。
このスレッドではすでにSeleniumについて何度も言及されているため(そして、それがどれほど遅くなるかについても言及されています)、他の2つの可能な解決策をリストします。
解決策1:これは Scrapyを使用してjavascriptで生成されたコンテンツをクロールする方法 に関する非常に素晴らしいチュートリアルであり、それ。
必要なもの:
Docker がマシンにインストールされています。これは、OSに依存しないプラットフォームを利用するため、この時点まで他のソリューションよりも優れています。
Install Splash は、対応するOSの手順に従ってください。
スプラッシュドキュメントからの引用:
スプラッシュは、javascriptレンダリングサービスです。 TwistedとQT5を使用してPython 3で実装された、HTTP APIを備えた軽量のWebブラウザーです。
基本的に、Splashを使用してJavascriptで生成されたコンテンツをレンダリングします。
スプラッシュサーバーを実行します:Sudo docker run -p 8050:8050 scrapinghub/splash
。
scrapy-splash プラグインをインストールします:pip install scrapy-splash
すでにScrapyプロジェクトが作成されていると仮定して(作成されていない場合は、 作成しましょう )、ガイドに従ってsettings.py
を更新します。
次に、スクレイピープロジェクトの
settings.py
にアクセスして、これらのミドルウェアを設定します。DOWNLOADER_MIDDLEWARES = { 'scrapy_splash.SplashCookiesMiddleware': 723, 'scrapy_splash.SplashMiddleware': 725, 'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810, }
スプラッシュサーバーのURL(WinまたはOSXを使用している場合、これはdockerマシンのURLである必要があります: ホストからDockerコンテナーのIPアドレスを取得する方法? ):
SPLASH_URL = 'http://localhost:8050'
最後に、これらの値も設定する必要があります。
DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter' HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'
最後に、 SplashRequest
を使用できます。
通常のスパイダーには、URLを開くために使用できるリクエストオブジェクトがあります。開きたいページにJS生成データが含まれている場合、SplashRequest(またはSplashFormRequest)を使用してページをレンダリングする必要があります。以下に簡単な例を示します。
class MySpider(scrapy.Spider): name = "jsscraper" start_urls = ["http://quotes.toscrape.com/js/"] def start_requests(self): for url in self.start_urls: yield SplashRequest( url=url, callback=self.parse, endpoint='render.html' ) def parse(self, response): for q in response.css("div.quote"): quote = QuoteItem() quote["author"] = q.css(".author::text").extract_first() quote["quote"] = q.css(".text::text").extract_first() yield quote
SplashRequestはURLをhtmlとしてレンダリングし、callback(parse)メソッドで使用できる応答を返します。
解決策2:現時点では実験的なものと呼びましょう(2018年5月)...
このソリューションは、Pythonのバージョン3.6専用です(現時点では)。
リクエスト モジュールを知っていますか(誰も知らない)。
今では、小さな兄弟をクロールするウェブがあります: requests-HTML :
このライブラリは、HTMLの解析(Webのスクレイピングなど)をできるだけシンプルかつ直感的にすることを目的としています。
Requests-htmlのインストール:pipenv install requests-html
ページのURLにリクエストを行います。
from requests_html import HTMLSession
session = HTMLSession()
r = session.get(a_page_url)
応答をレンダリングして、Javascriptで生成されたビットを取得します。
r.html.render()
最後に、モジュールは スクレイピング機能 を提供しているようです。
別の方法として、 BeautifulSoupを使用する を、レンダリングしたr.html
オブジェクトで、十分に文書化された方法で試すことができます。
多分 Selenium でできる。
from Selenium import webdriver
import time
driver = webdriver.Firefox()
driver.get(url)
time.sleep(5)
htmlSource = driver.page_source
以前にpythonにRequests
モジュールを使用したことがある場合、開発者がRequests-HTML
という新しいモジュールを作成したことがわかりました。このモジュールにはJavaScriptもレンダリングできるようになりました。
https://html.python-requests.org/ にアクセスしてこのモジュールの詳細を確認するか、JavaScriptのレンダリングにのみ興味がある場合は https:// html.python-requests.org/?#javascript-support モジュールを使用してPythonを使用してJavaScriptをレンダリングする方法を直接学習します。
基本的に、Requests-HTML
モジュールを正しくインストールすると、次の例、 上記のリンクに表示 は、このモジュールを使用してWebサイトをスクレイプし、Webサイト内に含まれるJavaScriptをレンダリングする方法を示します。
from requests_html import HTMLSession
session = HTMLSession()
r = session.get('http://python-requests.org/')
r.html.render()
r.html.search('Python 2 will retire in only {months} months!')['months']
'<time>25</time>' #This is the result.
私は最近、YouTubeビデオからこれについて学びました。 ここをクリックしてください! YouTubeビデオを見るには、モジュールの仕組みを示します。
素晴らしいブログ投稿 から取られた、これも良い解決策のようです
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from PyQt4.QtWebKit import *
from lxml import html
#Take this class for granted.Just use result of rendering.
class Render(QWebPage):
def __init__(self, url):
self.app = QApplication(sys.argv)
QWebPage.__init__(self)
self.loadFinished.connect(self._loadFinished)
self.mainFrame().load(QUrl(url))
self.app.exec_()
def _loadFinished(self, result):
self.frame = self.mainFrame()
self.app.quit()
url = 'http://pycoders.com/archive/'
r = Render(url)
result = r.frame.toHtml()
# This step is important.Converting QString to Ascii for lxml to process
# The following returns an lxml element tree
archive_links = html.fromstring(str(result.toAscii()))
print archive_links
# The following returns an array containing the URLs
raw_links = archive_links.xpath('//div[@class="campaign"]/a/@href')
print raw_links
Seleniumは、JSおよびAjaxコンテンツをスクレイピングするのに最適です。
Pythonを使用してWebからデータを抽出する については、この記事を確認してください
$ pip install Selenium
次に、Chrome webdriverをダウンロードします。
from Selenium import webdriver
browser = webdriver.Chrome()
browser.get("https://www.python.org/")
nav = browser.find_element_by_id("mainnav")
print(nav.text)
簡単ですね。
Webdriverを使用してjavascriptを実行することもできます。
from Selenium import webdriver
driver = webdriver.Firefox()
driver.get(url)
driver.execute_script('document.title')
または変数に値を保存します
result = driver.execute_script('var text = document.title ; return var')
ページのさまざまな部分に対して、スクリプトでurllib、要求、beautifulSoup、およびSelenium Webドライバーを使用することをお勧めします(いくつか例を挙げます)。
これらのモジュールの1つだけで必要なものを入手できる場合があります。
これらのモジュールの2つ、3つ、またはすべてが必要になる場合があります。
ブラウザのjsをオフにする必要がある場合があります。
スクリプトにヘッダー情報が必要になる場合があります。
。しかし、それらはすべて削ることができます!意志があるところには確実な方法があります。
将来的に継続的にスクレイピングされたデータが必要な場合は、必要なものをすべてスクレイピングし、ピクルスで.datファイルに保存します。
これらのモジュールで何を試すかを検索し、エラーをGoogleにコピーして貼り付けてください。
個人的には、scrapyとSeleniumを使用し、両方を別々のコンテナーにドッキングすることを好みます。この方法で、最小限の手間でインストールでき、ほとんどすべてが何らかの形でjavascriptを含む最新のWebサイトをクロールできます。以下に例を示します。
scrapy startproject
を使用してスクレーパーを作成し、スパイダーを作成すると、スケルトンは次のように簡単になります。
import scrapy
class MySpider(scrapy.Spider):
name = 'my_spider'
start_urls = ['https://somewhere.com']
def start_requests(self):
yield scrapy.Request(url=self.start_urls[0])
def parse(self, response):
# do stuff with results, scrape items etc.
# now were just checking everything worked
print(response.body)
本当の魔法はmiddlewares.pyで起こります。ダウンローダーミドルウェアの2つのメソッド__init__
およびprocess_request
を次の方法で上書きします。
# import some additional modules that we need
import os
from copy import deepcopy
from time import sleep
from scrapy import signals
from scrapy.http import HtmlResponse
from Selenium import webdriver
class SampleProjectDownloaderMiddleware(object):
def __init__(self):
Selenium_LOCATION = os.environ.get('Selenium_LOCATION', 'NOT_HERE')
Selenium_URL = f'http://{Selenium_LOCATION}:4444/wd/hub'
chrome_options = webdriver.ChromeOptions()
# chrome_options.add_experimental_option("mobileEmulation", mobile_emulation)
self.driver = webdriver.Remote(command_executor=Selenium_URL,
desired_capabilities=chrome_options.to_capabilities())
def process_request(self, request, spider):
self.driver.get(request.url)
# sleep a bit so the page has time to load
# or monitor items on page to continue as soon as page ready
sleep(4)
# if you need to manipulate the page content like clicking and scrolling, you do it here
# self.driver.find_element_by_css_selector('.my-class').click()
# you only need the now properly and completely rendered html from your page to get results
body = deepcopy(self.driver.page_source)
# copy the current url in case of redirects
url = deepcopy(self.driver.current_url)
return HtmlResponse(url, body=body, encoding='utf-8', request=request)
Settings.pyファイルの次の行のコメントを外して、このmiddlwareを有効にすることを忘れないでください。
DOWNLOADER_MIDDLEWARES = {
'sample_project.middlewares.SampleProjectDownloaderMiddleware': 543,}
ドッキングの次。軽量イメージからDockerfile
を作成し(ここではpython Alpineを使用しています)、プロジェクトディレクトリをコピーして、要件をインストールします。
# Use an official Python runtime as a parent image
FROM python:3.6-Alpine
# install some packages necessary to scrapy and then curl because it's handy for debugging
RUN apk --update add linux-headers libffi-dev openssl-dev build-base libxslt-dev libxml2-dev curl python-dev
WORKDIR /my_scraper
ADD requirements.txt /my_scraper/
RUN pip install -r requirements.txt
ADD . /scrapers
そして最後に、docker-compose.yaml
ですべてをまとめます:
version: '2'
services:
Selenium:
image: Selenium/standalone-chrome
ports:
- "4444:4444"
shm_size: 1G
my_scraper:
build: .
depends_on:
- "Selenium"
environment:
- Selenium_LOCATION=samplecrawler_Selenium_1
volumes:
- .:/my_scraper
# use this command to keep the container running
command: tail -f /dev/null
docker-compose up -d
を実行します。これを初めて行う場合、最新のSelenium/standalone-chromeを取得し、スクレーパーイメージもビルドするのに時間がかかります。
完了したら、docker ps
を使用してコンテナーが実行されていることを確認し、Seleniumコンテナーの名前がスクレーパーコンテナーに渡した環境変数の名前と一致することを確認できます(ここではSelenium_LOCATION=samplecrawler_Selenium_1
でした)。
docker exec -ti YOUR_CONTAINER_NAME sh
を使用してスクレーパーコンテナーに入り、コマンドはdocker exec -ti samplecrawler_my_scraper_1 sh
でした。正しいディレクトリにcdし、scrapy crawl my_spider
を使用してスクレーパーを実行します。
全体が私のgithubページにあり、 here から取得できます。
BeautifulSoupとSeleniumのミックスは、私にとって非常にうまく機能します。
from Selenium import webdriver
from Selenium.webdriver.common.by import By
from Selenium.webdriver.support.ui import WebDriverWait
from Selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup as bs
driver = webdriver.Firefox()
driver.get("http://somedomain/url_that_delays_loading")
try:
element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "myDynamicElement"))) #waits 10 seconds until element is located. Can have other wait conditions such as visibility_of_element_located or text_to_be_present_in_element
html = driver.page_source
soup = bs(html, "lxml")
dynamic_text = soup.find_all("p", {"class":"class_name"}) #or other attributes, optional
else:
print("Couldnt locate element")
追伸より多くの待機条件を見つけることができます こちら
PyQt5を使用
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import QUrl
from PyQt5.QtWebEngineWidgets import QWebEnginePage
import sys
import bs4 as bs
import urllib.request
class Client(QWebEnginePage):
def __init__(self,url):
global app
self.app = QApplication(sys.argv)
QWebEnginePage.__init__(self)
self.html = ""
self.loadFinished.connect(self.on_load_finished)
self.load(QUrl(url))
self.app.exec_()
def on_load_finished(self):
self.html = self.toHtml(self.Callable)
print("Load Finished")
def Callable(self,data):
self.html = data
self.app.quit()
#url = ""
#client_response = Client(url)
#print(client_response.html)