0

我正在使用 selenium 和 BeautifulSoup 从网站(http://www.grownjkids.gov/ParentsFamilies/ProviderSearch)中使用下一步按钮来抓取数据,我正在循环单击该按钮。我之前一直在与 StaleElementReferenceException 作斗争,但通过循环重新查找页面上的元素来克服这个问题。但是,我遇到了一个新问题-它现在可以一直单击到最后。但是当我检查它写入的 csv 文件时,即使大部分数据看起来不错,但通常会有 5 批重复的行(这是每页显示的结果数)。

我的意思的图片示例:https ://www.dropbox.com/s/ecsew52a25ihym7/Screen%20Shot%202019-02-13%20at%2011.06.41%20AM.png?dl=0

我有一种预感,这是因为我的程序每次尝试查找下一个按钮时都会重新提取页面上的当前数据。我很困惑为什么会发生这种情况,因为据我了解,实际的抓取部分只有在您跳出内部 while 循环后才会发生,该循环试图找到下一个按钮并进入较大的按钮。(如果我没有正确理解这一点,请告诉我,因为我对这些东西比较陌生。)

此外,我每次运行程序后输出的数据都是不同的(考虑到错误,这是有道理的,因为过去,StaleElementReferenceExceptions 发生在零星的位置。如果每次发生此异常时它都会重复结果,那么对于重复也会偶尔发生。更糟糕的是,每次我运行程序时都会跳过不同批次的结果 - 我交叉比较了来自 2 个不同输出的结果,并且有一些结果出现在一个而不是其他。

from selenium import webdriver
from selenium.webdriver.chrome.options import Options 
from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException
from bs4 import BeautifulSoup
import csv


chrome_options = Options()  
chrome_options.add_argument('--disable-gpu')
chrome_options.add_argument("--headless")  

url = "http://www.grownjkids.gov/ParentsFamilies/ProviderSearch"

driver = webdriver.Chrome('###location###')
driver.implicitly_wait(10)

driver.get(url)

#clears text box 
driver.find_element_by_class_name("form-control").clear()

#clicks on search button without putting in any parameters, getting all the results
search_button = driver.find_element_by_id("searchButton")
search_button.click()

df_list = []
headers = ["Rating", "Distance", "Program Type", "County", "License", "Program Name", "Address", "Phone", "Latitude", "Longitude"]

while True: 
    #keeps on clicking next button to fetch each group of 5 results 
    try:
        nextButton = driver.find_element_by_class_name("next")
        nextButton.send_keys('\n') 
    except NoSuchElementException: 
        break
    except StaleElementReferenceException:
        attempts = 0
        while (attempts < 100):
            try: 
                nextButton = driver.find_element_by_class_name("next")
                if nextButton:
                    nextButton.send_keys('\n') 
                    break
            except NoSuchElementException: 
                break
            except StaleElementReferenceException:
                attempts += 1

    #finds table of center data on the page
    table = driver.find_element_by_id("results")
    html_source = table.get_attribute('innerHTML')
    soup = BeautifulSoup(html_source, "lxml")

    #iterates through centers, extracting the data
    for center in soup.find_all("div", {"class": "col-sm-7 fields"}):
        mini_list = []
        #all fields except latlong
        for row in center.find_all("div", {"class": "field"}):
            material = row.find("div", {"class": "value"})
            if material is not None:
                mini_list.append(material.getText().encode("utf8").strip())
        #parses latlong from link
        for link in center.find_all('a', href = True):
            content = link['href']
            latlong = content[34:-1].split(',')
            mini_list.append(latlong[0])
            mini_list.append(latlong[1])

        df_list.append(mini_list)

#writes content into csv
with open ('output_file.csv', "wb") as f:
    writer = csv.writer(f)
    writer.writerow(headers)
    writer.writerows(row for row in df_list if row)

任何事情都会有帮助!如果您对我使用 selenium/BeautifulSoup/python 的方式有其他建议,以便改进我未来的编程,我将不胜感激。

非常感谢!

4

2 回答 2

0

我会使用 selenium 来获取结果计数,然后进行 API 调用以获取实际结果。如果结果计数大于pageSizeAPI 的 queryString 参数的限制,您可以分批循环并增加currentPage参数,直到达到总计数,或者,如下所示,只需一次请求所有结果。然后从json中提取你想要的。

import requests
import json
from bs4 import BeautifulSoup as bs
from selenium import webdriver

initUrl = 'http://www.grownjkids.gov/ParentsFamilies/ProviderSearch'
driver = webdriver.Chrome()
driver.get(initUrl)
numResults = driver.find_element_by_css_selector('#totalCount').text
driver.quit()
newURL = 'http://www.grownjkids.gov/Services/GetProviders?latitude=40.2171&longitude=-74.7429&distance=10&county=&toddlers=false&preschool=false&infants=false&rating=&programTypes=&pageSize=' + numResults + '&currentPage=0'
data = requests.get(newURL).json()

您有一组字典要在响应中迭代:

写出一些值的示例:

if(len(data)) > 0:
    for item in data:
        print(item['Name'], '\n' , item['Address'])

如果您担心 lat 和 long 值,您可以在使用 selenium 时从脚本标签之一中获取它们:

在此处输入图像描述

我用于 XHR jQuery GET 的备用 URL 您可以通过在页面上使用开发工具 (F12) 找到,然后使用 F5 刷新页面并检查在网络选项卡中发出的 jquery 请求:

在此处输入图像描述

于 2019-02-13T17:10:22.897 回答
0

您应该在 while 循环的每次迭代中阅读 HTML 内容。下面的例子:

while counter < oage_number_limit:
   counter = counter + 1
   new_data = driver.page_source
   page_contents = BeautifulSoup(new_data, 'lxml')
于 2020-05-10T11:41:07.590 回答