Webscraping - 试图提取一些数据,但陷入了最后的障碍

Posted

技术标签:

【中文标题】Webscraping - 试图提取一些数据,但陷入了最后的障碍【英文标题】:Webscraping - Trying to extract some data, but got stuck at the final hurdle 【发布时间】:2021-09-03 08:38:25 【问题描述】:

我已经设法公开了正确的数据(其中一些是在页面中动态计算的,所以比我想象的要复杂一些)但是我现在需要在 JSON 字符串中获取它,尽管我做了很多尝试卡住了!

这个 Python 脚本如下(使用 Selenium 和 BeautifulSoup):

from bs4 import BeautifulSoup
from selenium import webdriver
import datetime
from dateutil import parser
import requests
import json

url = 'https://www.braintree.gov.uk/bins-waste-recycling/route-3-collection-dates/1'

browser = webdriver.Chrome(executable_path = r'C:/Users/user/Downloads/chromedriver.exe')
browser.get(url)


html = browser.execute_script("return document.getElementsByTagName('html')[0].innerHTML")
soup = BeautifulSoup(html, "html.parser")

data=soup.find_all("div", "class":"date_display")
#print(data)
#out = 


for data in data:
    bin_colour = data.find('h3').text
    bin_date = parser.parse(data.find('p').text).strftime('%Y-%m-%d')
    print(bin_colour)
    print(bin_date)
    print()

browser.quit()

这会导致:

Grey Bin 

2021-06-30
   
Green Bin

2021-06-23
   
Clear Sack

2021-06-23

Food Bin

2021-06-23

它可能(可能)不是最好的代码/方法,所以我愿意接受您的建议。主要目标是:

"Grey Bin": "2021-06-30", "Green Bin": "2021-06-23", "Clear Sack": "2021-06-23", "Food Bin": "2021-06-23"

希望这是有道理的,我尝试了各种将数据转换为正确格式的方法,但似乎都丢失了,所以经过数小时的尝试后,我希望你们能提供帮助。

更新: MendelG 的两种解决方案都运行良好。 Vitalis 的解决方案提供了四个输出,最后一个是所需的输出 - 所以感谢你们提供非常快速和有效的解决方案 - 我很接近,但看不到树木的树木!

【问题讨论】:

【参考方案1】:

要获取字典格式的数据,可以尝试:

out = 
for data in tag:
    out[data.find("h3").text] = parser.parse(data.find("p").text).strftime("%Y-%m-%d")

print(out)

或者,使用字典理解:

print(
    
        data.find("h3").text: parser.parse(data.find("p").text).strftime("%Y-%m-%d")
        for data in tag
    
)

输出:

'Grey Bin': '2021-06-30', 'Green Bin': '2021-06-23', 'Clear Sack': '2021-06-23', 'Food Bin': '2021-06-23'

【讨论】:

好吧,我以为我有解决方案,而且输出看起来确实正确,但是看起来正确和正确必须是不同的事情。输出的重点是让 Home Assistant 读取它。我正在使用 json.dumps(out) 所以我得到每条记录的双引号,但家庭助理没有识别任何东西,我得到一个空的回报。 @StuartHodgson 这是一个不同的问题。我建议将其发布为一个新问题【参考方案2】:

您可以创建一个空字典,在其中添加值并打印它。

解决方案

from bs4 import BeautifulSoup
from selenium import webdriver
import datetime
from dateutil import parser
import requests
import json

url = 'https://www.braintree.gov.uk/bins-waste-recycling/route-3-collection-dates/1'

browser = webdriver.Chrome(executable_path='/snap/bin/chromium.chromedriver')
browser.get(url)


html = browser.execute_script("return document.getElementsByTagName('html')[0].innerHTML")
soup = BeautifulSoup(html, "html.parser")

data = soup.find_all("div", "class":"date_display")

result = 

for item in data:
    bin_colour = item.find('h3').text
    bin_date = parser.parse(item.find('p').text).strftime('%Y-%m-%d')
    result[bin_colour]=bin_date

print(result)

输出

'Grey Bin': '2021-06-30', 'Green Bin': '2021-06-23', 'Clear Sack': '2021-06-23', 'Food Bin': '2021-06-23'

如果你需要,你可以用类似的方式在list 中输出,但你需要.append 值,就像我在Trouble retrieving elements and looping pages using next page button 中所做的一样

如果您需要双引号,请使用此打印:

print(json.dumps(result))

它将打印:

"Grey Bin": "2021-06-30", "Green Bin": "2021-06-23", "Clear Sack": "2021-06-23", "Food Bin": "2021-06-23"

【讨论】:

【参考方案3】:

您可以使用requestsre 收集所有列出的日期。您正则表达式输出包含每个集合类型的日期的各种 javascript 对象。然后,您需要在每个月的值上加 1,以获得 1-12 范围内的月份;这可以通过正则表达式命名组来完成。这些可以转换为实际日期以供以后过滤。

最初将所有日期存储在字典中,其中键作为集合类型,值作为集合日期列表,您可以使用zip_longest 创建DataFrame。然后,您可以使用过滤来查找给定集合的下一个集合日期。

我使用了几个辅助函数来实现这一点。

import requests
from dateutil import parser
from datetime import datetime
from pandas import to_datetime, DataFrame
from itertools import zip_longest

def get_dates(dates):
    dates = [re.sub(r'(?P<g1>\d+),(?P<g2>\d+),(?P<g3>\d+)$', lambda d: parser.parse('-'.join([d.group('g1'), str(int(d.group('g2')) + 1), d.group('g3')])).strftime('%Y-%m-%d'), i) 
             for i in re.findall(r'Date\((\d4,\d1,2,\d1,2),', dates)] 
    dates = [datetime.strptime(i, '%Y-%m-%d').date() for i in dates]
    return dates

def get_next_collection(collection, df):
    return df[df[collection] >= to_datetime('today')][collection].iloc[0]
    
collection_types = ['grey', 'green', 'clear', 'food']
r = requests.get('https://www.braintree.gov.uk/bins-waste-recycling/route-3-collection-dates/1')
collections = 

for collection in collection_types:   
    dates = re.search(r'var 0(?:(?:bin)|(?:sack)) = (\[.*?\])'.format(collection), r.text, re.S).group(1)
    collections[collection] = get_dates(dates) 

df = DataFrame(zip_longest(collections['grey'], collections['green'], 
                           collections['clear'], collections['food']),
               columns = collection_types)

get_next_collection('grey', df)

您还可以使用生成器和islice,如@Martijn Pieters 所述,直接处理字典条目(保存收集日期)并限制您感兴趣的未来日期的数量,例如

filtered = (i for i in collections['grey'] if i >= date.today())
list(islice(filtered, 3))

更改的导入行是:

from itertools import zip_longest, islice
from datetime import datetime, date

然后,您不需要 pandas 导入或创建 DataFrame

【讨论】:

以上是关于Webscraping - 试图提取一些数据,但陷入了最后的障碍的主要内容,如果未能解决你的问题,请参考以下文章

python3 webscraping-循环只返回一次迭代

Chatgpt-3 使用的提取积累数据集技术和数据集自动化处理

Python 3.5 - 如何对javascript呈现的页面进行webscraping

Web Scraping:如何提取这种 div 标签?

Web Scraping - 带有日期选择器的表格作为图像

Scrapy了解(译)