用python抓取javascript网页

5gfr0r5j  于 2021-07-13  发布在  Java
关注(0)|答案(15)|浏览(423)

我正在尝试开发一个简单的网络刮板。我想提取没有html代码的文本。事实上,我实现了这个目标,但是我看到在一些加载javascript的页面中,我没有获得好的结果。
例如,如果一些javascript代码添加了一些文本,我就看不到它,因为当我调用

response = urllib2.urlopen(request)

我得到的原始文本没有添加的文本(因为javascript是在客户机中执行的)。
所以,我在想一些办法来解决这个问题。

lokaqttq

lokaqttq1#

美丽素和 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")

p、 你可以在这里找到更多的等待条件

kq4fsx7k

kq4fsx7k2#

您需要在脚本中为页面的不同部分使用urllib、requests、beautifulsoup和selenium web驱动程序(仅举几例)。
有时你只需要其中一个模块就可以得到你所需要的。
有时您需要两个、三个或所有这些模块。
有时你需要关闭浏览器上的js。
有时你需要在你的脚本标题信息。
没有一个网站可以以同样的方式被刮去,也没有一个网站可以永远以同样的方式刮去,而不必修改你的爬虫,通常是在几个月之后。但它们都可以刮!有志者事竟成。
如果将来需要不断地刮取数据,只需刮取所有需要的数据,并用pickle将其存储在.dat文件中。
只要继续搜索如何尝试这些模块和复制粘贴到谷歌你的错误。

wixjitnu

wixjitnu3#

使用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)
nqwrtyyt

nqwrtyyt4#

我两天来一直在试图找到这个问题的答案。许多答案会把你引向不同的问题。但蛇的答案上面是真正的重点。这是最短、最简单的解决方案。只是提醒一下,最后一个单词“var”代表变量名,因此应该用作:

result = driver.execute_script('var text = document.title ; return text')
jaql4c8m

jaql4c8m5#

如前所述,selenium是呈现javascript结果的好选择:

from selenium.webdriver import Firefox
from selenium.webdriver.firefox.options import Options

options = Options()
options.headless = True
browser = Firefox(executable_path="/usr/local/bin/geckodriver", options=options)

url = "https://www.example.com"
browser.get(url)

gazpacho是一个非常容易解析呈现html的库:

from gazpacho import Soup

soup = Soup(browser.page_source)
soup.find("a").attrs['href']
mwngjboj

mwngjboj6#

我最近使用html库来解决这个问题。
他们在readthedocs.io上的扩展文档相当不错(跳过pypi.org上的注解版本)。如果您的用例是基本的,那么您可能会获得一些成功。

from requests_html import HTMLSession
session = HTMLSession()
response = session.request(method="get",url="www.google.com/")
response.html.render()

如果在使用response.html.render()呈现所需数据时遇到问题,可以向render函数传递一些javascript来呈现所需的特定js对象。这是从他们的文档中复制的,但可能正是您需要的:
如果指定了脚本,它将在运行时执行提供的javascript。例子:

script = """
    () => {
        return {
            width: document.documentElement.clientWidth,
            height: document.documentElement.clientHeight,
            deviceScaleFactor: window.devicePixelRatio,
        }
    } 
"""

返回已执行脚本的返回值(如果提供):

>>> response.html.render(script=script)
{'width': 800, 'height': 600, 'deviceScaleFactor': 1}

在我的例子中,我想要的数据是填充javascript绘图的数组,但是数据在html中的任何地方都没有呈现为文本。有时,如果数据是动态填充的,则根本不清楚所需数据的对象名是什么。如果无法直接从view source或inspect中跟踪js对象,可以在浏览器(chrome)的调试器控制台中键入“window”,然后输入enter,以调出由浏览器呈现的对象的完整列表。如果你让一些受过教育的人

ivqmmu1c

ivqmmu1c7#

编辑2017年12月30日:这个答案出现在谷歌搜索的最热门结果中,所以我决定更新它。旧的答案还在后面。
dryscape不再维护,dryscape开发人员推荐的库只有python2。我发现将selenium的python库与phantomjs一起用作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驱动的网站。

使用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>
lztngnrs

lztngnrs8#

我们没有得到正确的结果,因为任何javascript生成的内容都需要在dom上呈现。当我们获取一个html页面时,我们获取初始的、未被javascript修改的dom。
因此,我们需要在抓取页面之前呈现javascript内容。
由于selenium在这个线程中已经被多次提到(有时也提到它有多慢),我将列出另外两种可能的解决方案。
解决方案1:这是一个非常好的教程,介绍如何使用scrapy对javascript生成的内容进行爬网,我们将遵循这一点。
我们需要:
docker安装在我们的机器上。在这之前,这是其他解决方案的优势,因为它使用的是独立于操作系统的平台。
按照相应操作系统的说明安装splash。
引用splash文档:
splash是一个javascript呈现服务。它是一个带有http api的轻量级web浏览器,在Python3中使用twisted和qt5实现。
本质上,我们将使用splash来呈现javascript生成的内容。
运行splash服务器: sudo docker run -p 8050:8050 scrapinghub/splash .
安装scrapy splash插件: pip install scrapy-splash 假设我们已经创建了一个残缺的项目(如果没有,让我们创建一个),我们将按照指南更新 settings.py :
那就去你的垃圾项目吧 settings.py 设置这些中间设备:

DOWNLOADER_MIDDLEWARES = {
      'scrapy_splash.SplashCookiesMiddleware': 723,
      'scrapy_splash.SplashMiddleware': 725,
      'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
}

splash服务器的url(如果您使用的是win或osx,这应该是docker机器的url:如何从主机获取docker容器的ip地址?):

SPLASH_URL = 'http://localhost:8050'

最后,您还需要设置这些值:

DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'
HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'

最后,我们可以使用 SplashRequest :
在普通的spider中,您可以使用请求对象打开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并返回可在回调(parse)方法中使用的响应。
解决方案2:现在(2018年5月)我们称之为实验性的。。。
此解决方案仅适用于python的3.6版(目前)。
你知道“请求”模块吗(谁不知道)?
现在它有了一个网络爬行的小兄弟:请求html:
该库旨在使解析html(例如,抓取网页)尽可能简单直观。
安装请求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 我们刚刚渲染的对象。

xkftehaa

xkftehaa9#

也许 selenium 能做到。

from selenium import webdriver
import time

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

fv2wmkja10#

如果你用过 Requests 模块之前,我最近发现开发人员创建了一个名为 Requests-HTML 它现在还可以呈现javascript。
您也可以访问https://html.python-requests.org/ 要了解有关此模块的更多信息,或者如果您只对呈现javascript感兴趣,则可以访问https://html.python-requests.org/?#javascript-支持直接学习如何使用模块使用python呈现javascript。
基本上,一旦正确安装 Requests-HTML 模块,下面的示例显示在上面的链接中,显示了如何使用此模块来刮取网站并呈现网站中包含的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视频。

deikduxw

deikduxw11#

听起来,您真正要查找的数据可以通过主页面上某些javascript调用的辅助url进行访问。
虽然您可以尝试在服务器上运行javascript来处理这个问题,但一种更简单的方法可能是使用firefox加载页面,并使用charles或firebug之类的工具来确定辅助url是什么。然后您可以直接查询该url以获取您感兴趣的数据。

46qrfjad

46qrfjad12#

这似乎也是一个很好的解决方案,从一个伟大的博客文章

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
ogq8wdun

ogq8wdun13#

selenium最适合于抓取js和ajax内容。
查看本文,了解如何使用python从web提取数据

$ pip install selenium

然后下载ChromeWebdriver。

from selenium import webdriver

browser = webdriver.Chrome()

browser.get("https://www.python.org/")

nav = browser.find_element_by_id("mainnav")

print(nav.text)

很简单,对吧?

y3bcpkx1

y3bcpkx114#

我个人更喜欢使用刮痧和 selenium 和码头都在不同的容器。通过这种方式,您可以以最小的麻烦安装,也可以对几乎都以某种形式包含javascript的现代网站进行爬网。举个例子:
使用 scrapy startproject 要创建scraper并编写spider, backbone 可以如下所示:

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)

真正的魔法发生在中间件中。覆盖downloader中间件中的两个方法, __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,}

接下来是dockerization。创建您的 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和构建你的scraper图像。
完成后,您可以检查您的容器是否正在运行 docker ps 还要检查selenium容器的名称是否与传递给scraper容器的环境变量的名称匹配(这里是 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页面上,你可以从这里得到它

dced5bon

dced5bon15#

您还可以使用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')

相关问题