python - PyQt5 QWebEnginePage - 可以编辑 HTML 以打开下拉列表吗?

标签 python html web-scraping beautifulsoup pyqt5

我正在尝试开发一个可持续的网络抓取脚本,以从网站获取所有产品的列表。产品类别链接位于网页的下拉(或可扩展)元素中。在提取 html 并使用 Beautiful Soup 将其转换为文本之前,我使用 PyQt5 来模拟客户端。

例如,如果您在浏览器上访问网站,则必须单击页面左上角附近的按钮才能打开从屏幕左侧弹出的类别列表(我将将其称为“边栏”)。在每个类别中,单击时都会有一个更具体类别的列表,每个类别都有一个我试图用我的代码获取的链接(我将这些称为“子类别”)。

即使侧栏被隐藏,初始类别列表元素也会出现在我的 Beautiful Soup 中,但子类别元素保持隐藏状态,除非子类别标题展开(因此,它们不会出现在我的汤中).我已通过手动检查 Chrome 浏览器中的元素来确认这一点。这是网页 HTML 的片段,其中包含我自己的评论以帮助解释:

<div aria-label="Fruits &amp; Vegetables" data-automation-id="taxonomy-toggle-Fruits &amp; Vegetables">
  <button aria-disabled="false" aria-expanded="false" class="NavSection__sectionBtn___1_cAs" data- 
   automation-id="nav-section-toggle" tabindex="-1"> #Initial category that contains sub-categories
  </button>
  <div>
  </div> #Contains the links I need, but doesn't populate HTML text unless sub-category element is expanded
</div>

下面是子类别元素展开后的样子:

<div aria-label="Fruits &amp; Vegetables" data-automation-id="taxonomy-toggle-Fruits &amp; Vegetables">
      <button aria-disabled="true" aria-expanded="true" class="NavSection__sectionBtn___1_cAs" data- 
       automation-id="nav-section-toggle" tabindex="-1"> #Initial category that contains sub-categories
      </button>
      <div>
         <ul class>
           <li class = "NavSection__sectionLink__rbr40> </li>
           <li class = "NavSection__sectionLink__rbr40> </li> #can open each li element up to acquire href link
           <li class = "NavSection__sectionLink__rbr40> </li>
         </ul>
      </div>
</div>

这是我的代码:

import bs4 as bs
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import QUrl
from PyQt5.QtWebEngineWidgets import QWebEnginePage

#act as a client via Qt5 to acquire javascript elements from webpage
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()

page = Page("https://grocery.walmart.com")
soup = bs.BeautifulSoup(page.html, 'lxml')
print(soup.prettify())

我知道如果aria-expandedaria-disabled <button> 的属性子类别 <li> 元素从“False”更改为“True”元素将出现在 HTML 中。我通过 Chrome 浏览器中的手动检查确认了这一点。

我的问题是是否有可能获得 href来自 <li>元素?我的假设是我必须编辑 HTML 才能更改 aria在初始解析后将属性从“False”更改为“True”,然后使用这些更改重新解析 HTML。如果没有,除了 Selenium 之外,还有其他方法可以从网页中获取这些元素吗?我正在尝试使用更精简的方法(不打开浏览器窗口等)。

我可以提供实际的网站 URL 和网页截图来帮助澄清,但不确定这是否被认为是好的做法或 Stack Overflow 是否允许(我是新来的!)。

有关我尝试使用的方法的更多背景信息,请参阅以下内容:

Sentdex's PyQt4 Dynamic Scraping Video

PyQt4 to PyQt5 library changes

最佳答案

如果您从页面下载 HTML,您会发现几乎整个页面都是使用 javascript 创建的,因此 Beautiful Soup 不是正确的工具,因为它仅用于分析 HTML。在这种情况下,解决方案是使用 runJavaScript() 通过 javascript 实现逻辑。 QWebEnginePage的方法:

from PyQt5 import QtCore, QtGui, QtWidgets, QtWebEngineWidgets


class WalmartGroceryPage(QtWebEngineWidgets.QWebEnginePage):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._results = None
        self.loadFinished.connect(self._on_load_finished)
        self.setUrl(QtCore.QUrl("https://grocery.walmart.com"))

    @QtCore.pyqtSlot(bool)
    def _on_load_finished(self, ok):
        if ok:
            self.runJavaScript(
                """
                function scraper_script(){
                    var results = []
                    self.document.getElementById("mobileNavigationBtn").click();
                    var elements = document.getElementsByClassName("NavSection__sectionBtn___1_cAs");
                    for (const element of elements) {
                        element.click();
                        var items = [];
                        var sub_elements = document.getElementsByClassName("MobileNavigation__navLink___2-m6_");
                        for (const e of sub_elements) {
                            var d = {"name": e.innerText, "url": e.href};
                            items.push(d);
                        }
                        var data = {"name": element.innerText, "items": items};
                        results.push(data);
                    }
                    return results;
                }
                scraper_script();
                """,
                self.results_callback,
            )

    def results_callback(self, value):
        self._results = value
        QtCore.QCoreApplication.quit()

    @property
    def results(self):
        return self._results


if __name__ == "__main__":
    import sys
    import json

    # sys.argv.append("--remote-debugging-port=8000")
    app = QtWidgets.QApplication(sys.argv)

    page = WalmartGroceryPage()
    ret = app.exec_()
    results = page.results

    print(json.dumps(results, indent=4))

输出:

[
    {
        "items": [
            {
                "name": "Fall Flavors Shop",
                "url": "https://grocery.walmart.com/cp/Flavors%20of%20Fall/9576778812"
            },
            {
                "name": "Baking Center",
                "url": "https://grocery.walmart.com/browse?shelfId=3433056320"
            },
            {
                "name": "Peak Season Produce",
                "url": "https://grocery.walmart.com/browse?shelfId=4881154845"
            },
# ...

关于python - PyQt5 QWebEnginePage - 可以编辑 HTML 以打开下拉列表吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58313185/

相关文章:

python - 使用 pandas 根据值按日期对数据进行分组

javascript - 文本在导出为字符串然后从 loadSVGFromString 导入后具有错误的 x 位置

html - 绝对定位的过渡

excel - 使用 Python Selenium 下载 Excel 文件

python - 如何从 html 源代码中具有相同属性集和相同层次结构的 2 个元素中抓取单个元素(使用 python 的漂亮汤)

jquery - 使用 Scrapy 通过无限滚动 Ajax 提取数据

python - biopython聚类的简单例子

python - 如何将列表更改为 HTML 表格? (Python)

python - 创建 dynamodb 表说 "invalid One or more parameter values were invalid: Some index key attributes are not defined in AttributeDefinitions"

javascript - 使用 javascript 更改页面内容无法再次返回主页