web-dev-qa-db-ja.com

Pythonを使用したWebスクレイピングJavaScriptページ

単純なWebスクレーパーを開発しようとしています。 HTMLコードなしでテキストを抽出したい。実際、私はこの目標を達成していますが、JavaScriptがロードされている一部のページでは、良い結果が得られないことがわかりました。

たとえば、JavaScriptコードがテキストを追加する場合、それを見ることができません。

response = urllib2.urlopen(request)

追加されたものなしで元のテキストを取得します(クライアントでJavaScriptが実行されるため)。

したがって、私はこの問題を解決するためのアイデアを探しています。

140
mocopera

編集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

JSサポートなしのスクレイピング:

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>

JSサポートによるスクレイピング:

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サイトをスクレイピングすることもできます。

JSサポートによるスクレイピング:

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>
184
avi

JavaScriptで生成されたコンテンツはすべてDOMでレンダリングする必要があるため、正しい結果が得られません。 HTMLページを取得するとき、JavaScriptによって変更されていない初期のDOMを取得します。

したがって、ページをクロールする前にJavaScriptコンテンツをレンダリングする必要があります。

このスレッドではすでにSeleniumについて何度も言及されているため(そして、それがどれほど遅くなるかについても言及されています)、他の2つの可能な解決策をリストします。


解決策1:これは Scrapyを使用してjavascriptで生成されたコンテンツをクロールする方法 に関する非常に素晴らしいチュートリアルであり、それ。

必要なもの:

  1. Docker がマシンにインストールされています。これは、OSに依存しないプラットフォームを利用するため、この時点まで他のソリューションよりも優れています。

  2. Install Splash は、対応するOSの手順に従ってください。
    スプラッシュドキュメントからの引用:

    スプラッシュは、javascriptレンダリングサービスです。 TwistedとQT5を使用してPython 3で実装された、HTTP APIを備えた軽量のWebブラウザーです。

    基本的に、Splashを使用してJavascriptで生成されたコンテンツをレンダリングします。

  3. スプラッシュサーバーを実行します:Sudo docker run -p 8050:8050 scrapinghub/splash

  4. scrapy-splash プラグインをインストールします:pip install scrapy-splash

  5. すでに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'
    
  6. 最後に、 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のスクレイピングなど)をできるだけシンプルかつ直感的にすることを目的としています。

  1. Requests-htmlのインストール:pipenv install requests-html

  2. ページのURLにリクエストを行います。

    from requests_html import HTMLSession
    
    session = HTMLSession()
    r = session.get(a_page_url)
    
  3. 応答をレンダリングして、Javascriptで生成されたビットを取得します。

    r.html.render()
    

最後に、モジュールは スクレイピング機能 を提供しているようです。
別の方法として、 BeautifulSoupを使用する を、レンダリングしたr.htmlオブジェクトで、十分に文書化された方法で試すことができます。

45
John Moutafis

多分 Selenium でできる。

from Selenium import webdriver
import time

driver = webdriver.Firefox()
driver.get(url)
time.sleep(5)
htmlSource = driver.page_source
40
amazingthere

以前に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ビデオを見るには、モジュールの仕組みを示します。

16
SShah

素晴らしいブログ投稿 から取られた、これも良い解決策のようです

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
14
marbel

あなたが本当に探しているデータは、プライマリページのJavaScriptによって呼び出されるセカンダリURLを介してアクセスできるようです。

サーバーでjavascriptを実行してこれを処理することもできますが、Firefoxを使用してページをロードし、 Charles または Firebug などのツールを使用して識別するのがより簡単な方法ですセカンダリURLとは何ですか。その後、興味のあるデータについてそのURLを直接照会することができます。

12
Stephen Emslie

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)

簡単ですね。

9
Macnux

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')
6
Serpentr

ページのさまざまな部分に対して、スクリプトでurllib、要求、beautifulSoup、およびSelenium Webドライバーを使用することをお勧めします(いくつか例を挙げます)。
これらのモジュールの1つだけで必要なものを入手できる場合があります。
これらのモジュールの2つ、3つ、またはすべてが必要になる場合があります。
ブラウザのjsをオフにする必要がある場合があります。
スクリプトにヘッダー情報が必要になる場合があります。
。しかし、それらはすべて削ることができます!意志があるところには確実な方法があります。
将来的に継続的にスクレイピングされたデータが必要な場合は、必要なものをすべてスクレイピングし、ピクルスで.datファイルに保存します。
これらのモジュールで何を試すかを検索し、エラーをGoogleにコピーして貼り付けてください。

5
user7780801

個人的には、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 から取得できます。

4
tarikki

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")

追伸より多くの待機条件を見つけることができます こちら

4
Biarys

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)
1
Ash-Ishh..