使用Python抓取Web页面

moc*_*era 154 python python-2.x web-scraping urlopen

我正在尝试开发一个简单的网络刮刀.我想在没有HTML代码的情况下提取文本.事实上,我实现了这个目标,但我已经看到在加载JavaScript的某些页面中我没有获得好的结果.

例如,如果某些JavaScript代码添加了一些文本,我看不到它,因为当我打电话时

response = urllib2.urlopen(request)
Run Code Online (Sandbox Code Playgroud)

我没有添加原始文本(因为JavaScript在客户端中执行).

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

avi*_*avi 190

编辑30/2017年12月:这个答案出现在谷歌搜索的最佳结果中,所以我决定更新它.旧答案仍在最后.

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

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

phantomjs --version
# result:
2.1.1
Run Code Online (Sandbox Code Playgroud)

举个例子,我创建了一个包含以下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>
Run Code Online (Sandbox Code Playgroud)

没有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>
Run Code Online (Sandbox Code Playgroud)

使用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'
Run Code Online (Sandbox Code Playgroud)

您还可以使用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>
Run Code Online (Sandbox Code Playgroud)

  • 值得注意的是PhantomJS已经停产,并且由于Chrome现在支持无头,因此不再处于积极开发阶段.建议使用无头镀铬/火狐. (15认同)
  • 可悲的是,没有Windows支持. (13认同)
  • 我收到以下警告:`PhantomJS 的 Selenium 支持已被弃用,请改用 Chrome 或 Firefox 的无头版本`。也许@sytech 在谈论 Selenium 对它的支持? (5认同)
  • 这是硒支持和PhantomJS本身.https://github.com/ariya/phantomjs/issues/15344 (3认同)
  • 这个答案在 2021 年也已经过时了...... (3认同)
  • `@Expenzor`我在Windows上工作.PhantomJS工作正常. (2认同)

Joh*_*fis 55

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

因此,我们需要在抓取页面之前呈现javascript内容.

由于硒已经多次在这个帖子中提到过(有时也提到它有多慢),我将列出另外两个可能的解决方案.


解决方案1:这是一个非常好的教程,介绍如何使用Scrapy抓取javascript生成的内容,我们将遵循这一点.

我们需要什么:

  1. Docker安装在我们的机器上.到目前为止,这是优于其他解决方案的优势,因为它使用独立于操作系统的平台.

  2. 按照我们相应操作系统列出的说明安装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项目(如果没有,让我们创建一个),我们将按照指南并更新settings.py:

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

    DOWNLOADER_MIDDLEWARES = {
          'scrapy_splash.SplashCookiesMiddleware': 723,
          'scrapy_splash.SplashMiddleware': 725,
          'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
    }
    
    Run Code Online (Sandbox Code Playgroud)

    Splash服务器的URL(如果你使用的是Win或OSX,这应该是docker机器的URL:如何从主机获取Docker容器的IP地址?):

    SPLASH_URL = 'http://localhost:8050'
    
    Run Code Online (Sandbox Code Playgroud)

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

    DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'
    HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'
    
    Run Code Online (Sandbox Code Playgroud)
  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
    
    Run Code Online (Sandbox Code Playgroud)

    SplashRequest将URL呈现为html并返回可在回调(parse)方法中使用的响应.


解决方案2:让我们在此时(2018年5月)调用此实验...
此解决方案仅适用于Python的3.6版本(目前).

你知道请求模块(好吧怎么没有)?
现在它有一个网络抓取小兄弟:requests-HTML:

该库旨在使解析HTML(例如,抓取Web)尽可能简单直观.

  1. 安装requests-html: pipenv install requests-html

  2. 请求页面的网址:

    from requests_html import HTMLSession
    
    session = HTMLSession()
    r = session.get(a_page_url)
    
    Run Code Online (Sandbox Code Playgroud)
  3. 渲染响应以获取Javascript生成的位:

    r.html.render()
    
    Run Code Online (Sandbox Code Playgroud)

最后,该模块似乎提供了抓取功能.
或者,我们可以尝试使用BeautifulSoupr.html我们刚刚渲染的对象的详细记录方式.

  • 我收到此错误:RuntimeError:无法在现有事件循环中使用 HTMLSession。请改用 AsyncHTMLSession。 (3认同)
  • @HuckIt这似乎是一个已知问题:https://github.com/psf/requests-html/issues/140 (2认同)

ama*_*ere 42

也许可以做到这一点.

from selenium import webdriver
import time

driver = webdriver.Firefox()
driver.get(url)
time.sleep(5)
htmlSource = driver.page_source
Run Code Online (Sandbox Code Playgroud)

  • `options = webdriver.ChromeOptions() options.add_argument('--headless') driver = webdriver.Chrome(options=options)` (3认同)
  • Selenium对于此类事情确实很繁重,如果您不使用PhantomJS,它会不必要地变慢,并且需要一个浏览器头,但这是可行的。 (2认同)

SSh*_*hah 17

如果您以前曾经使用过Requestspython模块,我最近发现开发人员创建了一个新模块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.
Run Code Online (Sandbox Code Playgroud)

我最近从YouTube视频中了解到了这一点.点击这里!观看YouTube视频,演示该模块的工作原理.

  • 请注意,此模块仅支持Python 3.6. (2认同)
  • 似乎在幕后使用铬。不过对我来说效果很好 (2认同)
  • 也适用于 3.9,这意味着它适用于 3.6 及更高版本。 (2认同)
  • 域“http://python-requests.org/”已关闭,如果您可以更新您的答案以演示“.search”的确切作用,那就太好了。 (2认同)

mar*_*bel 15

这似乎也是一个很好的解决方案,来自一篇很棒的博客文章

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
Run Code Online (Sandbox Code Playgroud)


Ste*_*lie 12

听起来您正在寻找的数据可以通过主页面上的某些javascript调用的辅助URL进行访问.

虽然您可以尝试在服务器上运行javascript来处理这个问题,但更简单的方法可能是使用Firefox加载页面并使用像CharlesFirebug这样的工具来确定辅助URL的确切内容.然后,您可以直接查询该URL,以获取您感兴趣的数据.


Mac*_*nux 10

Selenium是抓取JS和Ajax内容的最佳选择.

查看本文以使用Python从Web中提取数据

$ pip install selenium
Run Code Online (Sandbox Code Playgroud)

然后下载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)
Run Code Online (Sandbox Code Playgroud)

容易,对吗?


小智 7

您也可以使用webdriver执行javascript.

from selenium import webdriver

driver = webdriver.Firefox()
driver.get(url)
driver.execute_script('document.title')
Run Code Online (Sandbox Code Playgroud)

或将值存储在变量中

result = driver.execute_script('var text = document.title ; return var')
Run Code Online (Sandbox Code Playgroud)


tar*_*kki 6

我个人更喜欢在单独的容器中使用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)
Run Code Online (Sandbox Code Playgroud)

真正的魔力发生在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)
Run Code Online (Sandbox Code Playgroud)

不要忘记通过取消注释settings.py文件中的以下行来启用此中间件软件:

DOWNLOADER_MIDDLEWARES = {
'sample_project.middlewares.SampleProjectDownloaderMiddleware': 543,}
Run Code Online (Sandbox Code Playgroud)

接下来进行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
Run Code Online (Sandbox Code Playgroud)

最后将它们整合到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
Run Code Online (Sandbox Code Playgroud)

运行docker-compose up -d。如果您是第一次这样做,将需要一段时间来获取最新的Selenium / standalone-chrome,并生成您的scraper图像。

完成后,您可以检查容器是否与之一起运行,docker ps还可以检查硒容器的名称是否与我们传递给我们的scraper容器的环境变量(此处为SELENIUM_LOCATION=samplecrawler_selenium_1)相匹配。

输入您的刮板容器docker exec -ti YOUR_CONTAINER_NAME sh,对我来说,命令是docker exec -ti samplecrawler_my_scraper_1 shcd进入正确的目录,然后使用来运行您的刮板scrapy crawl my_spider

整个内容都在我的github页面上,您可以从这里获取


Ash*_*h.. 6

使用 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)
Run Code Online (Sandbox Code Playgroud)


小智 5

您需要在脚本的页面不同部分(仅举几例)中使用urllib,requests,beautifulSoup和selenium Web驱动程序。
有时,仅使用这些模块之一就可以满足您的需求。
有时您需要两个,三个或所有这些模块。
有时您需要关闭浏览器上的js。
有时,您的脚本中需要标题信息。
通常,几个月后,无需修改爬网程序,就无法永久删除相同的网站,也无法永久永久删除相同的网站。但是它们都可以被刮掉!有意志的地方肯定有办法。
如果您需要在未来持续不断地抓取数据,则只需抓取您需要的所有内容并将其存储在pickle中的.dat文件中即可。
只需继续搜索如何尝试使用这些模块,然后将错误复制并粘贴到Google中即可。


Bia*_*rys 5

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")
Run Code Online (Sandbox Code Playgroud)

PS您可以在这里找到更多等待条件


归档时间:

查看次数:

198252 次

最近记录:

5 年,12 月 前