使用 Python 抓取 JavaScript 页面
我正在尝试开发一个简单的网络抓取工具。我想在没有HTML代码的情况下提取文本。它适用于纯HTML,但不适用于JavaScript代码添加文本的某些页面。
例如,如果一些JavaScript代码添加了一些文本,我看不到它,因为当我调用时:
response = urllib2.urlopen(request)
我得到的原始文本没有添加一个(因为JavaScript是在客户端中执行的)。
所以,我正在寻找一些解决这个问题的想法。
我正在尝试开发一个简单的网络抓取工具。我想在没有HTML代码的情况下提取文本。它适用于纯HTML,但不适用于JavaScript代码添加文本的某些页面。
例如,如果一些JavaScript代码添加了一些文本,我看不到它,因为当我调用时:
response = urllib2.urlopen(request)
我得到的原始文本没有添加一个(因为JavaScript是在客户端中执行的)。
所以,我正在寻找一些解决这个问题的想法。
编辑2021年9月:不再维护phantomjs
编辑30 / Dec / 2017:此答案出现在Google搜索的顶级结果中,因此我决定对其进行更新。旧的答案仍然在最后。
dryscape不再维护,库dryscape开发人员建议只有Python 2。我发现使用Selenium的python库和Phantom JS作为Web驱动程序足够快,很容易完成工作。
安装 Phantom JS 后,请确保二进制文件在当前路径中可用:phantomjs
phantomjs --version
# result:
2.1.1
#Example 举个例子,我使用以下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,它说:和javascript:No javascript support
Yay! Supports javascript
不支持 JS 的#Scraping:
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 的#Scraping:
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 的#Scraping:
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内容。
由于硒在这个线程中已经多次提到(有时还提到了它的速度有多慢),我将列出另外两种可能的解决方案。
解决方案 1:这是一个非常好的教程,介绍如何使用Scrapy来抓取javascript生成的内容,我们将遵循这一点。
我们需要:
Docker 安装在我们的机器中。到目前为止,这是其他解决方案的加分项,因为它利用了独立于操作系统的平台。
按照为相应的操作系统列出的说明安装 Splash。
引用来自 Splash 文档:
Splash是一个javascript渲染服务。它是一个带有HTTP API的轻量级Web浏览器,使用Twisted和QT5在Python 3中实现。
从本质上讲,我们将使用Splash来渲染Javascript生成的内容。
运行初始服务器:。sudo docker run -p 8050:8050 scrapinghub/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, }
Splash 服务器的 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,并返回可以在回调(解析)方法中使用的响应。
解决方案 2:让我们暂时称之为实验(2018年5月)...
此解决方案仅适用于Python的3.6版本(目前)。
您知道请求模块吗(好吧,谁不知道)?
现在它有一个网络爬行小兄弟:requests-HTML:
该库旨在使解析HTML(例如抓取Web)尽可能简单直观。
Install requests-html:pipenv install requests-html
向页面的网址发出请求:
from requests_html import HTMLSession
session = HTMLSession()
r = session.get(a_page_url)
渲染响应以获取 Javascript 生成的位:
r.html.render()
最后,该模块似乎提供了抓取功能。
或者,我们可以尝试将 BeautifulSoup 与我们刚刚渲染的对象一起使用的有据可查的方式。r.html