代码之家  ›  专栏  ›  技术社区  ›  Jack Armstrong

带隐藏弹出窗口的Web Scrape,不显示任何信息

  •  0
  • Jack Armstrong  · 技术社区  · 4 年前

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

    response = urllib2.urlopen(request)
    

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

    0 回复  |  直到 5 年前
        1
  •  218
  •   avi    7 年前

    编辑2017年12月30日:这个答案出现在谷歌搜索的最热门结果中,所以我决定更新它。旧的答案还在后面。

    一旦你安装了 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>
    

    No javascript support 使用javascript: Yay! Supports javascript

    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'
    

    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>
    
        2
  •  86
  •   John Moutafis Milani Igor    5 年前

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

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

    由于selenium在这个线程中已经被多次提到(有时也提到它有多慢),我将列出另外两种可能的解决方案。


    解决方案1: how to use Scrapy to crawl javascript generated content 我们将遵循这一点。

    我们需要:

    1. Docker

    2. Install Splash 按照我们相应操作系统的说明。

      Splash是一个javascript呈现服务。它是一个带有httpapi的轻量级web浏览器,在python3中使用Twisted和QT5实现。

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

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

    5. 假设我们已经创建了一个残缺的项目(如果没有, let's make one ),我们将按照指南更新 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 :

      在普通的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
      


    解决方案2: 我们现在就称之为实验性的(2018年5月)。。。
    只是(目前)。

    你知道 requests 模块(谁不呢)?
    requests-HTML :

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

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

    2. 向页面的url发出请求:

      from requests_html import HTMLSession
      
      session = HTMLSession()
      r = session.get(a_page_url)
      
    3. r.html.render()
      

    最后,这个模块似乎提供了 scraping capabilities .
    of using BeautifulSoup r.html

        3
  •  52
  •   amazingthere    8 年前

    selenium 我能做到。

    from selenium import webdriver
    import time
    
    driver = webdriver.Firefox()
    driver.get(url)
    time.sleep(5)
    htmlSource = driver.page_source
    
        4
  •  24
  •   SShah Marc B    6 年前

    如果你用过 Requests 模块之前,我最近发现开发人员创建了一个名为 Requests-HTML 它现在还可以呈现JavaScript。

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

    模块,下面的示例 shown on the above link

    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视频。

        5
  •  15
  •   Stephen Emslie    13 年前

    虽然您可以尝试在服务器上运行javascript来处理这个问题,但一种更简单的方法可能是使用Firefox加载页面并使用类似 Charles Firebug 以确定辅助URL是什么。然后您可以直接查询该URL以获取您感兴趣的数据。

        6
  •  15
  •   Robbie    7 年前

    这似乎也是一个很好的解决方案,从 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
    
        7
  •  12
  •   seco    6 年前

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

    extracting data from the web using Python

    $ pip install selenium
    

    from selenium import webdriver
    
    browser = webdriver.Chrome()
    
    browser.get("https://www.python.org/")
    
    nav = browser.find_element_by_id("mainnav")
    
    print(nav.text)
    

    很简单,对吧?

        8
  •  8
  •   Serpentr    7 年前

    您还可以使用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')
    
        9
  •  8
  •   tarikki    6 年前

    我个人更喜欢使用刮痧和硒和码头都在不同的容器。通过这种方式,您可以以最小的麻烦安装,也可以对几乎都以某种形式包含javascript的现代网站进行爬网。举个例子:

    使用 scrapy startproject 要创建scraper并编写spider,骨架可以如下所示:

    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)
    

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

    不要忘记通过取消注释设置.py文件:

    DOWNLOADER_MIDDLEWARES = {
    'sample_project.middlewares.SampleProjectDownloaderMiddleware': 543,}
    

    接下来是dockerization。创建您的 Dockerfile

    # 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 scrapy crawl my_spider .

    整个东西都在我的github页面上,你可以从 here

        10
  •  6
  •   Biarys    6 年前

    美丽素和硒的混合对我很有效。

    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、 你可以找到更多的等待条件 here

        11
  •  4
  •   user7780801 user7780801    7 年前

    您需要在脚本中为页面的不同部分使用urllib、requests、beautifulSoup和selenium web驱动程序(仅举几例)。

    有时您需要两个、三个或所有这些模块。
    有时你需要关闭浏览器上的js。

    没有一个网站可以以同样的方式被刮去,也没有一个网站可以永远以同样的方式刮去,而不必修改你的爬虫,通常是在几个月之后。但它们都可以刮!有志者事竟成。
    如果将来需要不断地刮取数据,只需刮取所有需要的数据,并用pickle将其存储在.dat文件中。
    只要继续搜索如何尝试这些模块和复制粘贴到谷歌你的错误。

        12
  •  4
  •   Ash Ishh    5 年前

    使用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)
    
        13
  •  1
  •   Abd_bgc    5 年前

    我两天来一直在试图找到这个问题的答案。许多答案会把你引向不同的问题。但是 serpentr's 上面的答案很中肯。这是最短、最简单的解决方案。只是提醒一下最后一句话 “变量” 表示 ,所以应该用作:

     result = driver.execute_script('var text = document.title ; return text')
    
        14
  •  1
  •   emehex    4 年前

    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

    from gazpacho import Soup
    
    soup = Soup(browser.page_source)
    soup.find("a").attrs['href']
    
        15
  •  1
  •   Digestible1010101    4 年前

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

    expanded documentation 在读取docs.io很好(跳过注释版本)pypi.org网站). 如果您的用例是基本的,那么您可能会获得一些成功。

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

    运行时。例子:

    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,以调出由浏览器呈现的对象的完整列表。如果您对数据存储的位置进行一些有根据的猜测,您可能会在那里找到它。我的图表数据window.view.data文件在控制台中,因此在传递给上面引用的.render()方法的“script”变量中,我使用了:

    return {
        data: window.view.data
    }