首页 > 解决方案 > 使用 python selenium 读取、写入和控制动态实例化的 HTML web 表

问题描述

假设有一些特定的搜索者搜索一些商品,我用“泰迪”搜索。总结果数为 140,并显示在由<div>每行和每列组成的小表格中(每个内容为行,内容为信息的列),并带有滚动条。这向我展示了一个很好的列表,最多可显示 5 个(每个内容的高度为 40px),如果我需要查看更多内容,我需要向下滚动此表。

如果我在第 45 到第 49 处看到商品(第 45 条内容位于当前视图的顶部),则 HTML 如下所示。

<div class="table-body" style="height:200px">            // This contains scrollbar
    <div class="table-panel" style="height:5600px">
        <div class="ag-row" style="height:40px row="42"> // This is each row of goods
            <div class="name">Teddy</div>                // This is each column of good
            <div class="price">200</div>
            <input class="amount">0</input>              // Input text box for put amount of goods to buy
        </div>
        <div class="ag-row" style="height:40px row="43">
            <div class="name">Brown Bess</div>
            <div class="price">230</div>
            <input class="amount">0</input>
        </div>
        <div class="ag-row" style="height:40px row="44"> // <-- This is what I'am seeing at the top. 0 based row attribute
            <div class="name">Blue</div>
            <div class="price">280</div>
            <input class="amount">0</input>
        </div>
        <div class="ag-row" style="height:40px row="45">
            <div class="name">Scientist</div>
            <div class="price">400</div>
            <input class="amount">0</input>
        </div>
        <div class="ag-row" style="height:40px row="46">
            <div class="name">Mouse</div>
            <div class="price">120</div>
            <input class="amount">0</input>
        </div>
        <div class="ag-row" style="height:40px row="47">
            <div class="name">Hangover</div>
            <div class="price">150</div>
            <input class="amount">0</input>
        </div>
        <div class="ag-row" style="height:40px row="48"> // <-- This is what I'am seeing last.
            <div class="name">Building</div>
            <div class="price">420</div>
            <input class="amount">0</input>
        </div>
        <div class="ag-row" style="height:40px row="50">
            <div class="name">Park</div>
            <div class="price">60</div>
            <input class="amount">0</input>
        </div>
        <div class="ag-row" style="height:40px row="51">
            <div class="name">Coffee</div>
            <div class="price">160</div>
            <input class="amount">0</input>
        </div>
        <div class="ag-row" style="height:40px row="49">
            <div class="name">Juice</div>
            <div class="price">100</div>
            <input class="amount">0</input>
        </div>
    </div>
</div>

这也是我的虚构代码,实际代码由于其样式、属性和脚本而复杂得多。我认为问我的主题就足够了。

我检查了这个网页的行为。它只会使它成为我所看到的附近的 html。当我看到接近第 100 个内容时,它会在第 92 到 108 个之间创建 html——实例化的数量是非常随机的。当我向下或向上滚动时,它会删除远离当前位置的内容并为当前屏幕创建新内容。

我需要解析这些数据,并且需要在 python 中创建一些类似列表的数据结构。因为它根据屏幕实例化部分数据(准确地说,它似乎使用滚动条来检查我看到的位置)我试图控制滚动条并裁剪 html 中的所有数据并删除重复项。代码如下

from selenium import webdriver
..blah..

def iterateOptionTable(driver):
    el_viewport = driver.find_element_by_class_name('table-body')
    driver.execute_script('document.getElementsByClassName("{}")[0].scrollTop = 0;'.format('table-body'))
    max_height = int(driver.execute_script('return document.getElementsByClassName("{}")[0].scrollHeight;'.format('table-body')))
    scrolling_amnt = int(40 * 5) # Each row height is 40
    cur_scroll = 0
    table = defaultdict(int) # Don't put into list which is already pushed
    ret = []
    while cur_scroll < max_height:
            el_products = el_viewport.find_elements_by_xpath('./div/*')
            for el_p in el_products:
                rownum = int(el_p.get_attribute("row"))
                if rownum not in table:
                    table[rownum] = True
                    ret.append(el_p)
            yield ret   # List of WebElement of good
            ret.clear()        
            cur_scroll += scrolling_amnt
            driver.execute_script('document.getElementsByClassName("{}")[0].scrollTop = {};'.format('table-body', cur_scroll))

def parseElementToData(elems):
    ret = []
    for el in elems:
        single_data = DO_EXTRACT_DATA_FROM_EL()
        ret.append(single_data)

def parseTable(driver):
    ret = []
    for elems in iterateOptionTable(driver):
        data += parseElementToData(elems)
    return ret

该页面还有其他几个作业,yield由于网页层次结构,它是使用编程的。

当我一一执行时,它在调试器中运行良好。但在实际运行时,它甚至不会向下滚动它的表格。更不用说我认为它效率低下。还通过从 selenium 执行脚本尝试了相同版本的 Javascript。

是否有更复杂的方法,或者我可以得到为什么这些在正常情况下不起作用的答案。我对网络爬行和硒很陌生。请帮忙 :)

标签: pythonseleniumweb-crawler

解决方案


您可以看到元素的事实并不意味着它们已经在 HTML 中,它们必须具有 display: hidden 直到您滚动到它们。

现在我在这里假设,因为您没有提供相关网页的链接,我将尝试用您提供的代码进行解释。

我的建议是将表 1 中的所有行按 1 返回:

i = 0
row_list = []

while True:
    try:
        name = driver.find_element_by_xpath(x_path_to_the_row[i]/div).get_attribute('innerHTML'
        price = driver.find_element_by_xpath(x_path_to_the_row[i]/div[2]).get_attribute('innerHTML')
        row_list.append((name, price))
    except NoSuchElementException:
        break
    i += 1

基本上循环直到表的元素不存在,获取该行的列并构造一个包含两个元素的元组。

注意:除非 HTML 位于 Shadow DOM 组件中,否则应该不会有问题。


推荐阅读