response = urllib2.urlopen(request)
时,我得到的原始文本没有添加的文本(因为在客户端中执行了JavaScript)。
因此,我正在寻找一些解决此问题的想法。
#1 楼
编辑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支持的抓取: br />
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>
评论
可悲的是,没有Windows支持。
– Expenzor
17年4月17日在14:39
Windows中编程的人还有其他选择吗?
– Hoshiko86
17年5月5日在19:54
@Expenzor我正在Windows上工作。 PhantomJS正常工作。
– Aakash Choubey
18年1月12日在10:43
值得注意的是,鉴于Chrome现在支持无头,PhantomJS已停产并且不再处于积极开发中。建议使用无头铬/ Firefox。
– sytech
18 Mar 23 '18 at 20:42
它既是硒的支持,又是PhantomJS本身。 github.com/ariya/phantomjs/issues/15344
– sytech
18年4月30日在12:34
#2 楼
我们无法获得正确的结果,因为任何JavaScript生成的内容都需要在DOM上呈现。当我们获取HTML页面时,我们获取的是未经javascript修改的初始DOM。因此,我们需要在爬网页面之前呈现javascript内容。
作为硒在该线程中已经被提到很多次了(有时也提到它变得很慢),我将列出其他两个可能的解决方案。解决方案1:这是一个非常不错的教程,如何使用Scrapy爬网javascript生成的内容,我们将遵循这一点。
我们需要的是:
我们的机器中安装了Docker。到目前为止,这是对其他解决方案的加分,因为它利用了与操作系统无关的平台。
按照针对我们相应的操作系统列出的说明安装Splash。从splash文档引用:
Splash是一种javascript呈现服务。这是一个带有HTTP API的轻型网络浏览器,使用Twisted和QT5在Python 3中实现。
基本上,我们将使用Splash呈现Javascript生成的内容。
运行Splash服务器:
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,
}
Splash服务器的URL(如果您使用的是Win或OSX,则应为docker计算机:如何从主机获取Docker容器的IP地址?):
SPLASH_URL = 'http://localhost:8050'
最后,您还需要设置以下值:
DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'
HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'
最后,我们可以使用
SplashRequest
:在普通蜘蛛中,您可以使用Request对象来打开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)方法中使用的响应。
您知道请求模块(谁不知道)吗?
现在它具有一个可在网络上爬行的小兄弟:requests-HTML:
该库旨在使解析HTML(例如,抓取Web)尽可能简单直观。
安装请求-html:
pipenv install requests-html
向页面的网址:
from requests_html import HTMLSession
session = HTMLSession()
r = session.get(a_page_url)
呈现响应以获取Javascript生成的位:
r.html.render()
最后,该模块似乎提供了抓取功能。
或者,我们可以尝试使用我们很好地将
r.html
对象与BeautifulSoup结合使用的文献证明的方法。错了。评论
在调用.render()之后,如何扩展获取JS位的完整HTML内容?在那之后我被困住了。我没有看到通常从r.html.html对象中的JavaScript注入到页面中的所有iframe。
–anon58192932
18/12/13在20:24
@ anon58192932由于目前这是一个实验性的解决方案,因此我不知道您要尝试实现的目标是什么,所以我真的无法提出任何建议...如果您还没有这样做,可以在此处创建一个新问题制定了解决方案
–约翰·穆塔菲斯(John Moutafis)
19年1月2日,13:57
我收到此错误:RuntimeError:无法在现有事件循环中使用HTMLSession。请改用AsyncHTMLSession。
–约书亚·斯塔福德(Joshua Stafford)
19年4月23日在15:59
@HuckIt这似乎是一个已知问题:github.com/psf/requests-html/issues/140
–约翰·穆塔菲斯(John Moutafis)
19-10-15在12:22
#3 楼
硒也许可以做到。from selenium import webdriver
import time
driver = webdriver.Firefox()
driver.get(url)
time.sleep(5)
htmlSource = driver.page_source
评论
Selenium在这种情况下确实很沉重,这会不必要地变慢,并且如果您不使用PhantomJS,则需要一个浏览器头,但这是可行的。
–约书亚树篱
17年7月28日在16:27
@JoshuaHedges您可以在无头模式下运行其他更多标准浏览器。
– reynoldsnlp
1月9日0:55
options = webdriver.ChromeOptions()options.add_argument('-headless')driver = webdriver.Chrome(options = options)
–奇妙
10月15日14:50
#4 楼
如果您以前曾经将Requests
模块用于python,我最近发现开发人员创建了一个名为Requests-HTML
的新模块,该模块现在还具有呈现JavaScript的功能。您还可以访问https:// /html.python-requests.org/了解有关此模块的更多信息,或者如果您仅对呈现JavaScript感兴趣,则可以访问https://html.python-requests.org/?#javascript-support直接学习如何
基本上,一旦正确安装了
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视频,该视频演示了该模块的工作原理。
评论
请注意,此模块仅支持Python 3.6。
–nat5142
18-10-12在15:56
我收到此错误:SSLError:HTTPSConnectionPool(host ='docs.python-requests.org',端口= 443):url超过了最大重试次数:/(由SSLError(SSLError(1,'[[SSL:TLSV1_ALERT_INTERNAL_ERROR]内部错误(_ssl.c:1045)')))
–约书亚·斯塔福德(Joshua Stafford)
19年4月23日在16:01
@HuckIt应用程序我不熟悉该错误,但是该错误似乎是您尝试访问的网站可能存在与SSL证书相关的问题。抱歉,这不是解决方案,但是我建议您在堆栈溢出中提出一个新问题(如果尚未提出),并可能提供更多详细信息,例如您正在使用的网站url和代码。
–Shah
19年4月28日在18:57
似乎在引擎盖下使用铬。虽然对我来说很棒
–Sid
4月27日11:46
#5 楼
听起来好像您真正要寻找的数据可以通过主页上某些javascript调用的辅助URL进行访问。虽然您可以尝试在服务器上运行javascript来处理此问题,但这种方法比较简单可能是使用Firefox加载页面并使用诸如Charles或Firebug之类的工具来准确识别该辅助URL是什么。然后,您可以直接在该URL中查询您感兴趣的数据。
评论
@Kris以防万一有人偶然发现这个问题并想尝试它而不是像硒这样重的东西,这是一个简短的例子。这将在McMaster-Carr网站上打开六角螺母的零件详细信息页面。他们的网站内容大部分是使用Javascript获取的,并且几乎没有本机页面信息。如果打开浏览器开发人员工具,导航至“网络”选项卡,然后刷新页面,则可以查看该页面发出的所有请求并找到相关数据(在本例中为部件详细信息html)。
–SweepingsDemon
18年8月13日在18:02
这是在Firefox devtool的“网络”选项卡中找到的另一个URL,如果遵循该URL,则包含大多数零件信息的html,并公开了一些必需的参数,这些参数可轻松导航到其他零件信息,从而更易于抓取。这个价格示例不是特别有用,因为价格是由另一个Javascript函数生成的,但是对于想要遵循Stephen的建议的任何人来说,它应该可以很好地用作介绍。
–SweepingsDemon
18年8月13日在18:10
#6 楼
摘自一篇出色的博客文章,这似乎也是一个不错的解决方案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
#7 楼
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)
容易,对吧?
评论
硒是最好的,但是某些站点似乎可以检测到硒的使用情况。
–贾瓦德·艾哈迈德·汗
8月26日5:21
硒是最好的,但是某些站点似乎可以检测到硒的使用情况。
–贾瓦德·艾哈迈德·汗
8月26日5:21
#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 var')
评论
或者您可以只使用driver.title属性
– Corey Goldberg
18年6月17日在22:39
#9 楼
我个人更喜欢在单独的容器中使用scrapy和硒,并进行docker化。通过这种方式,您可以轻松地安装和抓取几乎所有都以一种形式或另一种形式包含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,}
下一步用于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以及构建刮板图像。 完成后,您可以检查容器是否正在使用
docker ps
运行,还可以检查硒容器的名称是否与我们传递给刮板容器的环境变量的名称匹配(此处是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页面上你可以从这里得到它
#10 楼
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.S.您可以在这里找到更多等待条件
#11 楼
您可能希望在脚本中针对页面的不同部分使用urllib,requests,beautifulSoup和selenium Web驱动程序(仅举几例)。有时,仅使用这些模块之一即可获得所需的内容
有时您需要两个,三个或所有这些模块。
有时您需要关闭浏览器上的js。
有时您需要输入标头信息您的脚本。
通常不需要几个月的时间,而无需修改您的搜寻器,就无法永久删除相同的网站,也无法永久永久删除相同的网站。但是它们都可以被刮掉!哪里有办法,肯定有办法。
如果您需要在未来持续不断地抓取数据,只需将所需的所有内容抓取,然后用泡菜将其存储在.dat文件中。
请继续搜索如何尝试使用这些模块,并将您的错误复制并粘贴到Google中。
#12 楼
使用PyQt5from 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)
#13 楼
我一直在努力寻找针对此问题的答案两天。许多答案将您引向不同的问题。但是,蛇形人的上述回答确实是关键。这是最短,最简单的解决方案。提醒一下,最后一个词“ var”代表变量名,因此应用作: result = driver.execute_script('var text = document.title ; return text')
评论
这应该是对蛇的答案的评论,而不是单独的答案。
–塞尔比乌斯(Yserbius)
1月29日20:04
很明显但是,我还没有50名代表对别人的回答发表评论。
– Abd_bgc
2月2日,19:10
#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']
评论
听起来您可能需要更重的东西,请尝试使用Selenium或Watir。我已经在Java中成功完成了此操作(我使用了Cobra工具包lobobrowser.org/cobra.jsp)。由于您想入侵python(总是一个不错的选择),因此我建议您使用以下两个选项:-packtpub.com/article/使用python-part-2进行网络抓取-blog.databigbang.com/web-scraping-ajax-and-javascript-sites