【问题标题】:Scrape dynamically loaded website with python用python抓取动态加载的网站
【发布时间】:2020-04-06 07:50:43
【问题描述】:

我是抓取动态加载网站的新手,我一直在尝试抓取该网站的团队名称和几率

https://www.cashpoint.com/de/fussball/deutschland/bundesliga

我在这篇文章中使用 PyQt5 进行了尝试

PyQt4 to PyQt5 -> mainFrame() deprecated, need fix to load web pages

class Page(QWebEnginePage):

    def __init__(self, url):
        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, html_str):
        self.html = html_str
        self.app.quit()

def main():

    page = Page('https://www.cashpoint.com/de/fussball/deutschland/bundesliga')
    soup = bs.BeautifulSoup(page.html, 'html.parser')
    js_test = soup.find('div', class_='game__team game__team__football')
    print(js_test.text)

if __name__ == '__main__': main()

但它不适用于我要抓取的网站。我得到一个, AttributeError: 'NoneType' object has no attribute 'text' Error。我没有使用这种方法获取网站的内容,尽管在上面的帖子中有一个为动态加载的网站编写的方法。正如我所读到的,在处理动态加载的网站时,第一种方法是确定数据在页面上的呈现方式。我该怎么做,为什么 PyQt5 不适用于这个网站? Selenium 的方式对我来说不是一个选择,因为它会太慢而无法获得实时赔率。当我检查网站以使用它时,我能否获得网站的 html 内容,然后以 Beautifulsoup 或 Scrapy 的正常方式使用它?提前谢谢你。

【问题讨论】:

  • 为什么硒太慢了?它应该能够以秒为单位加载页面,这将是这里最简单的解决方案。
  • 正如论坛上有人提到的,Selenium 使用大量资源,当您使用线程并行抓取 50 个站点时,它会变慢,不是吗?

标签: javascript python web-scraping beautifulsoup pyqt5


【解决方案1】:

提供的代码失败,因为即使页面完成加载新元素也是异步创建的,例如您想要获取“game__team”和“game__team__football”的 div,所以在发出 loadFinished 信号时,即使这些元素也是未创建。

一种可能的解决方案是直接使用javascript通过runJavaScript()方法获取文本列表,如果列表为空则在时间T再试一次,直到列表不为空。

import sys

from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets


class Scrapper(QtCore.QObject):
    def __init__(self, interval=500, parent=None):
        super().__init__(parent)

        self._result = []
        self._interval = interval

        self.page = QtWebEngineWidgets.QWebEnginePage(self)
        self.page.loadFinished.connect(self.on_load_finished)
        self.page.load(
            QtCore.QUrl("https://www.cashpoint.com/de/fussball/deutschland/bundesliga")
        )

    @property
    def result(self):
        return self._result

    @property
    def interval(self):
        return self._interval

    @interval.setter
    def interval(self, interval):
        self._interval = interval

    @QtCore.pyqtSlot(bool)
    def on_load_finished(self, ok):
        if ok:
            self.execute_javascript()
        else:
            QtCore.QCoreApplication.exit(-1)

    def execute_javascript(self):
        self.page.runJavaScript(
            """
        function text_by_classname(classname){ 
            var texts = [];
            var elements = document.getElementsByClassName(classname);
            for (const e of elements) {
                texts.push(e.textContent);
            }
            return texts;
        }
        [].concat(text_by_classname("game__team"), text_by_classname("game__team__football"));
        """,
            self.javascript_callback,
        )

    def javascript_callback(self, result):
        if result:
            self._result = result
            QtCore.QCoreApplication.quit()
        else:
            QtCore.QTimer.singleShot(self.interval, self.execute_javascript)


def main():
    app = QtWidgets.QApplication(sys.argv)
    scrapper = Scrapper(interval=1000)
    app.exec_()
    result = scrapper.result
    del scrapper, app

    print(result)


if __name__ == "__main__":
    main()

输出:

[' 1899 Hoffenheim ', ' FC Augsburg ', ' Bayern München ', ' Werder Bremen ', ' Hertha BSC ', ' SC Freiburg ', ' 1. Fsv Mainz 05 ', ' Borussia Dortmund ', ' 1. FC Köln ', ' Bayer 04 Leverkusen ', ' SC Paderborn ', ' FC Union Berlin ', ' Fortuna Düsseldorf ', ' RB Leipzig ', ' VFL Wolfsburg ', ' Borussia Mönchengladbach ', ' FC Schalke 04 ', ' Eintracht Frankfurt ', ' Werder Bremen ', ' 1. Fsv Mainz 05 ', ' Borussia Dortmund ', ' RB Leipzig ', ' FC Augsburg ', ' Fortuna Düsseldorf ', ' FC Union Berlin ', ' 1899 Hoffenheim ', ' Bayer 04 Leverkusen ', ' Hertha BSC ', ' Borussia Mönchengladbach ', ' SC Paderborn ', ' VFL Wolfsburg ', ' FC Schalke 04 ', ' Eintracht Frankfurt ', ' 1. FC Köln ', ' SC Freiburg ', ' Bayern München ', ' 1899 Hoffenheim ', ' Borussia Dortmund ', ' Bayern München ', ' VFL Wolfsburg ', ' 1899 Hoffenheim ', ' Bayern München ', ' Hertha BSC ', ' 1. Fsv Mainz 05 ', ' 1. FC Köln ', ' SC Paderborn ', ' Fortuna Düsseldorf ', ' VFL Wolfsburg ', ' FC Schalke 04 ', ' Werder Bremen ', ' Borussia Dortmund ', ' FC Augsburg ', ' FC Union Berlin ', ' Bayer 04 Leverkusen ', ' Borussia Mönchengladbach ', ' VFL Wolfsburg ', ' Eintracht Frankfurt ', ' SC Freiburg ', ' 1899 Hoffenheim ', ' Bayern München ']

【讨论】:

  • 感谢您的回答。我仍然不确定 text_by_classname 中的类名是如何从 [].concat(text_by_classname("game__team"), text_by_classname("game__team__football")) 获取的。以及如何 document.getElementsByClassName(classname);加载我需要的内容。我搜索了 runJavaScript 的文档,但找不到任何解释。也许你能告诉我你从哪里学到的?
  • @rickyspanish 那是javascript代码,与Qt/PyQt无关。请参阅developer.mozilla.org/en-US/docs/Web/API/Document/…developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…。 runJavaScript 是一个 QWebEnginePage 方法,它允许您运行 javascript 并在回调中返回结果。如果你打算进入报废的世界,我建议你学习 HTML 和 javascript,以及它是如何工作的。
【解决方案2】:

我给你的建议是使用 Selenium 作为解决方案:

pip 安装硒

from selenium import webdriver
from bs4 import BeautifulSoup as soup

driver = webdriver.Firefox(executable_path = '/Users/alireza/Downloads/geckodriver')
driver.get(URL)
driver.maximize_window()
page_source = driver.page_source
page_soup = soup(page_source, 'html.parser')

js_test = page_soup.find("div", {"class":"game__team game__team__football"})
print(js_test.text)

你可以从here下载geckodriver

如果您想查看示例代码,可以查看here 这是 www.tripadvisor.com 的网络爬虫。 希望这会有所帮助。

【讨论】:

  • 感谢您的回答。我会尝试一下,但我是否误会 selenium 使用大量 cpu 资源并且比其他方法慢?
  • @rickyspanish 我没有找到更好的库来完成这项任务,因为我在研究中经常使用它,所以我没有遇到任何极端的 CPU 使用率。
猜你喜欢
  • 1970-01-01
  • 2018-08-09
  • 1970-01-01
  • 1970-01-01
  • 2021-04-17
  • 2010-09-17
  • 1970-01-01
相关资源
最近更新 更多