【问题标题】:Web-scraping JavaScript page with Python使用 Python 抓取网页的 JavaScript 页面
【发布时间】:2026-02-13 20:40:02
【问题描述】:

我正在尝试开发一个简单的网络爬虫。我想提取没有 HTML 代码的文本。其实我实现了这个目标,但是我看到在一些加载了 JavaScript 的页面中我并没有得到很好的结果。

例如,如果一些 JavaScript 代码添加了一些文本,我看不到它,因为当我调用时

response = urllib2.urlopen(request)

我得到了没有添加的原始文本(因为 JavaScript 是在客户端执行的)。

所以,我正在寻找一些想法来解决这个问题。

【问题讨论】:

  • 听起来你可能需要更重的东西,试试 Selenium 或 Watir。
  • 我已经在 J​​ava 中成功地做到了这一点(我使用了 Cobra 工具包lobobrowser.org/cobra.jsp)因为你想在 python 中破解(总是一个不错的选择)我推荐这两个选项:-@987654322 @ - blog.databigbang.com/web-scraping-ajax-and-javascript-sites
  • 请注意,top-rated answer 最后一次更新是在 2017 年,由于 PhantomJS 和 dryscrape 已被弃用,因此自 2021 年起已过时。我建议在尝试它推荐的一种技术之前阅读整个线程。

标签: python web-scraping python-2.x urlopen


【解决方案1】:

2021 年 9 月编辑:phantomjs 也不再维护

编辑 2017 年 12 月 30 日:此答案出现在 Google 搜索的热门结果中,因此我决定对其进行更新。旧答案仍在最后。

dryscape 不再维护,dryscape 开发人员推荐的库仅是 Python 2。我发现使用 Selenium 的 python 库和 Phantom JS 作为 Web 驱动程序足够快并且很容易完成工作。

安装Phantom JS 后,确保phantomjs 二进制文件在当前路径中可用:

phantomjs --version
# result:
2.1.1

#示例 举个例子,我用下面的 HTML 代码创建了一个示例页面。 (link):

<!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

#Scraping 不支持 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>

#Scraping 与 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 驱动的网站。

#Scraping 与 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>

【讨论】:

  • 很遗憾,不支持 Windows。
  • @Expenzor 我在 Windows 上工作。 PhantomJS 工作正常。
  • 值得注意的是 PhantomJS 已经停产,并且不再积极开发,因为 Chrome 现在支持无头。建议使用无头 chrome/firefox。
  • 我收到以下警告:Selenium support for PhantomJS has been deprecated, please use headless versions of Chrome or Firefox instead。也许@sytech 是在谈论 Selenium 对它的支持?
  • 它既是 selenium 支持,也是 PhantomJS 本身。 github.com/ariya/phantomjs/issues/15344
【解决方案2】:

我们没有得到正确的结果,因为任何 javascript 生成的内容都需要在 DOM 上呈现。当我们获取一个 HTML 页面时,我们会获取初始的、未经 javascript 修改的 DOM。

因此我们需要在抓取页面之前渲染 javascript 内容。

由于 selenium 在此线程中已多次提及(有时还提到了它的速度有多慢),我将列出其他两种可能的解决方案。


解决方案 1: 这是关于 how to use Scrapy to crawl javascript generated content 的一个非常好的教程,我们将照此进行。

我们需要什么:

  1. Docker 安装在我们的机器上。在此之前,这是一个优于其他解决方案的优势,因为它利用了一个独立于操作系统的平台。

  2. Install Splash 按照我们相应操作系统列出的说明进行操作。
    引自启动文档:

    Splash 是一个 javascript 渲染服务。它是一个带有 HTTP API 的轻量级 Web 浏览器,使用 Twisted 和 QT5 在 Python 3 中实现。

    基本上我们将使用 Splash 来呈现 Javascript 生成的内容。

  3. 运行启动服务器:sudo docker run -p 8050:8050 scrapinghub/splash

  4. 安装scrapy-splash插件:pip install scrapy-splash

  5. 假设我们已经创建了一个 Scrapy 项目(如果没有,let's make one),我们将按照指南更新settings.py

    然后去你的scrapy项目的settings.py设置这些中间件:

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

    Splash 服务器的 URL(如果您使用的是 Win 或 OSX,这应该是 docker 机器的 URL:How to get a Docker container's IP address from the host?):

    SPLASH_URL = 'http://localhost:8050'
    

    最后你也需要设置这些值:

    DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'
    HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'
    
  6. 最后,我们可以使用SplashRequest

    在一个普通的蜘蛛中,你有可以用来打开 URL 的 Request 对象。如果您要打开的页面包含 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 模块吗(谁不知道)?
现在它有了一个网络爬虫小兄弟:requests-HTML:

这个库旨在使解析 HTML(例如抓取网页)尽可能简单和直观。

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

最后,该模块似乎提供了scraping capabilities
或者,我们可以使用我们刚刚渲染的r.html 对象尝试使用有据可查的方式of using BeautifulSoup

【讨论】:

  • 您能否详细说明如何在调用 .render() 后获取完整的 HTML 内容,并加载 JS 位?在那之后我被困住了。我没有看到通常从 r.html.html 对象中的 JavaScript 注入页面的所有 iframe。
  • @anon58192932 因为目前这是一个实验性解决方案,我不知道你究竟想要达到什么结果,所以我无法提出任何建议......你可以创建一个新问题如果您还没有找到解决方案,请点击此处
  • 我收到此错误:RuntimeError: Cannot use HTMLSession within an existing event loop。请改用 AsyncHTMLSession。
  • @HuckIt 这似乎是一个已知问题:github.com/psf/requests-html/issues/140
【解决方案3】:

也许selenium可以做到。

from selenium import webdriver
import time

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

【讨论】:

  • Selenium 对于这种事情来说真的很重,如果你不使用 PhantomJS,那会不必要地慢,并且需要一个浏览器头,但这会起作用。
  • @JoshuaHedges 你可以在无头模式下运行其他更标准的浏览器。
  • options = webdriver.ChromeOptions() options.add_argument('--headless') driver = webdriver.Chrome(options=options)
【解决方案4】:

如果您之前曾经使用过 Requests 模块用于 python,我最近发现开发人员创建了一个名为 Requests-HTML 的新模块,它现在也可以渲染 JavaScript。

您也可以访问https://html.python-requests.org/ 了解有关此模块的更多信息,或者如果您只对渲染 JavaScript 感兴趣,则可以访问 https://html.python-requests.org/?#javascript-support 直接学习如何使用该模块使用 Python 渲染 JavaScript。

基本上,一旦你正确安装了Requests-HTML 模块,下面的例子,shown on the above link,展示了你如何使用这个模块来抓取网站并渲染网站中包含的 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 视频中了解到这一点。 Click Here! 观看 YouTube 视频,该视频演示了该模块的工作原理。

【讨论】:

  • 应该注意这个模块只支持 Python 3.6。
  • 似乎在引擎盖下使用铬。不过对我来说效果很好
  • 也适用于 3.9,这意味着它适用于 3.6 及更高版本。
  • 在树莓派上运行良好。只需链接到本机 Chromium 浏览器。 *.com/questions/66588194/…
【解决方案5】:

听起来您真正要查找的数据可以通过主页上的某些 javascript 调用的辅助 URL 访问。

虽然您可以尝试在服务器上运行 javascript 来处理此问题,但更简单的方法可能是使用 Firefox 加载页面并使用 CharlesFirebug 之类的工具来准确识别辅助 URL 是什么。然后,您可以直接在该 URL 中查询您感兴趣的数据。

【讨论】:

  • @Kris 以防万一有人偶然发现并想尝试它而不是像硒这样重的东西,这里有一个简短的例子。 This 将在 McMaster-Carr 网站上打开六角螺母的零件详细信息页面。他们的网站内容主要是使用 Javascript 获取的,并且本地页面信息很少。如果您打开浏览器开发者工具,导航到 Network 选项卡,然后刷新页面,您可以看到页面发出的所有请求并找到相关数据(在本例中为部件详细信息 html)。
  • This 是在 Firefox devtool 网络选项卡中找到的不同 URL,如果遵循该 URL,则包含大部分部件信息的 html,并公开了轻松导航到其他部件信息所需的一些参数为了更容易刮。这个特定的例子并不是特别有用,因为价格是由另一个 Javascript 函数生成的,但应该足以作为任何想要遵循斯蒂芬建议的人的介绍。
【解决方案6】:

这似乎也是一个很好的解决方案,取自great blog post

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

【讨论】:

  • 只是要注意qtwebkit实际上是在运行浏览器,因此使用selenium/webdriver的开销较低,并且可以得到相同的结果
  • 来自 PyQt > 5 QtWebKit 弃用 QtWebEngineWidgets
【解决方案7】:

Selenium 最适合抓取 JS 和 Ajax 内容。

查看这篇文章以获取extracting data from the web using Python

$ pip install selenium

然后下载 Chrome 网络驱动程序。

from selenium import webdriver

browser = webdriver.Chrome()

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

nav = browser.find_element_by_id("mainnav")

print(nav.text)

简单吧?

【讨论】:

  • 硒是最好的,但有些网站似乎检测到硒的使用。
【解决方案8】:

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

【讨论】:

  • 或者你可以使用driver.title属性
【解决方案9】:

我个人更喜欢在单独的容器中使用 scrapy 和 selenium 和 dockerizing。通过这种方式,您既可以轻松安装,也可以抓取几乎所有包含一种或另一种形式的 JavaScript 的现代网站。这是一个例子:

使用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。覆盖下载器中间件中的两个方法__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 文件中的下一行来启用此中间件:

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进入你的scraper容器,我的命令是docker exec -ti samplecrawler_my_scraper_1 sh,cd到正确的目录并用scrapy crawl my_spider运行你的scraper。

整个内容都在我的 github 页面上,你可以从 here 获得它

【讨论】:

  • 这看起来真的是过多的码头化。
【解决方案10】:

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

附:你可以找到更多的等待条件here

【讨论】:

  • BeautifulSoup 有什么用? Selenium 已经有选择器并且可以在实时页面上使用。
  • @ggorlen 提取文本或其他数据。 Selenium 选择器用于导航页面上的元素。我用的时候就是这样。
  • Selenium 也可以在元素被选中后提取数据。在此页面上查看许多答案,例如this
【解决方案11】:

使用 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,谢谢!这是对我有用的解决方案,因为 selenium 对于这样一个简单的任务来说有点矫枉过正,而 requests-html 仅适用于 python 3.6。我会推荐这个解决方案而不是其他任何解决方案。
【解决方案12】:

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

【讨论】:

    【解决方案13】:

    Pyppeteer

    您可以考虑Pyppeteer,这是Chrome/Chromium 驱动前端Puppeteer 的Python 端口。

    下面是一个简单的例子,展示了如何使用 Pyppeteer 访问动态注入页面的数据:

    import asyncio
    from pyppeteer import launch
    
    async def main():
        browser = await launch({"headless": True})
        [page] = await browser.pages()
    
        # normally, you go to a live site...
        #await page.goto("http://www.example.com")
        # but for this example, just set the HTML directly:
        await page.setContent("""
        <body>
        <script>
        // inject content dynamically with JS, not part of the static HTML!
        document.body.innerHTML = `<p>hello world</p>`; 
        </script>
        </body>
        """)
        print(await page.content()) # shows that the `<p>` was inserted
    
        # evaluate a JS expression in browser context and scrape the data
        expr = "document.querySelector('p').textContent"
        print(await page.evaluate(expr, force_expr=True)) # => hello world
    
        await browser.close()
    
    asyncio.get_event_loop().run_until_complete(main())
    

    Pyppeteer's reference docs

    【讨论】:

      【解决方案14】:

      如前所述,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']
      

      【讨论】:

        【解决方案15】:

        我最近使用 requests_html 库来解决这个问题。

        他们在 readthedocs.io 上的 expanded documentation 非常好(跳过 pypi.org 上的注释版本)。如果您的用例是基本的,您可能会取得一些成功。

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

        如果您在使用 response.html.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 中的任何位置呈现为文本。有时,如果动态填充数据,则根本不清楚您想要的数据的对象名称是什么。如果您无法直接从查看源代码或检查中跟踪 js 对象,您可以在浏览器 (Chrome) 的调试器控制台中键入“window”,然后按 ENTER 以调出浏览器呈现的对象的完整列表。如果您对数据的存储位置进行了一些有根据的猜测,那么您可能会很幸运地找到它。我的图形数据在控制台中的 window.view.data 下,所以在传递给上面引用的 .render() 方法的“脚本”变量中,我使用了:

        return {
            data: window.view.data
        }
        

        【讨论】:

        • 似乎 requests_html 不再积极维护(最后更新于 2020 年 5 月)。它使用pyppeteer 进行渲染,这似乎得到了积极维护;它使用 Chromium 在下面进行渲染。
        【解决方案16】:

        尝试直接访问 API

        您将在抓取中看到的一个常见情况是网页从 API 端点异步请求数据。以下网站就是一个最小的例子:

        <body>
        <script>
        fetch("https://jsonplaceholder.typicode.com/posts/1")
          .then(res => {
            if (!res.ok) throw Error(res.status);
            
            return res.json();
          })
          .then(data => {
            // inject data dynamically via JS after page load
            document.body.innerText = data.title;
          })
          .catch(err => console.error(err))
        ;
        </script>
        </body>

        在许多情况下,API 将受到 CORS 或访问令牌的保护,或者受到严格的速率限制,但在其他情况下,它是公开访问的,您可以完全绕过网站。对于 CORS 问题,您可以尝试cors-anywhere

        一般过程是使用浏览器的开发人员工具的网络选项卡来搜索页面发出的请求,以查找要抓取的数据的关键字/子字符串。通常,您会看到带有 JSON 有效负载的未受保护的 API 请求端点,您可以使用 urllibrequests 模块直接访问该端点。上述可运行的 sn-p 就是这种情况,您可以使用它来练习。单击“运行 sn-p”后,我在网络选项卡中找到端点的方法如下:

        这个例子是人为的;通过查看静态标记,端点 URL 可能并不明显,因为它可以动态组装、缩小和隐藏在许多其他请求和端点下。网络请求还将显示任何相关的请求负载详细信息,例如您可能需要的访问令牌。

        获取端点 URL 和相关详细信息后,使用标准 HTTP 库在 Python 中构建请求并请求数据:

        >>> import requests
        >>> res = requests.get("https://jsonplaceholder.typicode.com/posts/1")
        >>> data = res.json()
        >>> data["title"]
        'sunt aut facere repellat provident occaecati excepturi optio reprehenderit'
        

        当您可以侥幸成功时,这往往比在您阅读本文时使用 Selenium、Pyppeteer、Scrapy 或任何流行的抓取库抓取页面更容易、更快且更可靠。

        如果您不走运并且数据没有通过以良好格式返回数据的 API 请求到达,它可能是原始浏览器有效负载的一部分,位于 &lt;script&gt; 标记中,可以是 JSON 字符串或(更有可能)一个 JS 对象。例如:

        <body>
        <script>
          var someHardcodedData = {
            userId: 1,
            id: 1,
            title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit', 
            body: 'quia et suscipit\nsuscipit recusandae con sequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto'
          };
          document.body.textContent = someHardcodedData.title;
        </script>
        </body>

        没有一种万能的方法来获取这些数据。基本技术是使用 BeautifulSoup 访问 &lt;script&gt; 标记文本,然后应用正则表达式或解析来提取对象结构、JSON 字符串或数据可能采用的任何格式。这是关于上图示例结构:

        import json
        import re
        from bs4 import BeautifulSoup
        
        # pretend we've already used requests to retrieve the data, 
        # so we hardcode it for the purposes of this example
        text = """
        <body>
        <script>
          var someHardcodedData = {
            userId: 1,
            id: 1,
            title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit', 
            body: 'quia et suscipit\nsuscipit recusandae con sequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto'
          };
          document.body.textContent = someHardcodedData.title;
        </script>
        </body>
        """
        soup = BeautifulSoup(text, "lxml")
        script_text = str(soup.select_one("script"))
        pattern = r"title: '(.*?)'"
        print(re.search(pattern, script_text, re.S).group(1))
        

        查看这些资源以解析不是完全有效的 JSON 的 JS 对象:

        以下是一些额外的案例研究/概念验证,其中使用 API 绕过了抓取:

        如果一切都失败了,请尝试此线程中列出的众多动态抓取库之一。

        【讨论】:

          【解决方案17】:

          简单快捷的解决方案:

          我正在处理同样的问题。我想抓取一些用 JavaScript 构建的数据。如果我只用 BeautifulSoup 从这个网站上抓取文本,那么我会以文本中的标签结束。 我想渲染这个标签并愿意从中获取信息。 另外,我不想使用像 Scrapy 和 selenium 这样的重型框架。

          所以,我发现请求moduleget方法需要url,它实际上是渲染脚本标签。

          例子:

          import requests
          custom_User_agent = "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0"
          url = "https://www.abc.xyz/your/url"
          response = requests.get(url, headers={"User-Agent": custom_User_agent})
          html_text = response.text
          

          这将呈现加载站点并呈现标签。

          希望这将有助于快速轻松地呈现加载了脚本标签的网站。

          【讨论】:

          • 您能否在答案中包含提供脚本标签呈现的示例网站?
          • 这显然不能解决 OP 要求的任何问题..
          • 查看&lt;script&gt;标签的文本和实际执行其中的JS是有区别的。这是前者,而不是后者。
          最近更新 更多