【问题标题】:Python BeautifulSoup print from just table cellPython BeautifulSoup 仅从表格单元格打印
【发布时间】:2020-06-22 16:38:10
【问题描述】:

我是美丽汤的新手。我正在尝试获取一个可以抓取网页的 python 脚本,然后打印一个精简列表。到目前为止,我有:

from bs4 import BeautifulSoup
import requests

URL = 'https://shropshire.gov.uk/waste/binday/index.jsc?p=0&go=Go&designation=3&postcode=sy3+9jt&gobutton=Go'
content = requests.get(URL)

soup = BeautifulSoup(content.text, 'html.parser')

current = soup.find("div", {"class": "maxi calendar_month"})

print(current.text)

我的问题是:

  1. 如何让 B/S 仅从表格单元格(即不在 HTML 中)读取?
  2. 如何压缩此列表以删除换行符?

理想情况下,我希望将输出存储为 python 字典

谢谢

【问题讨论】:

  • 我不确定您面临什么问题,您希望数据结构清晰,但您正在寻找的理想输出是什么?到目前为止,我可以看到实现是正确的,但你不能做什么?
  • 嗨,理想的输出将通过字典或类似的简单 dict { 1: "Recycling", 2: "", 3: "Garden Waste" } 等,因此仅从单元格中获取值在表格中(不是标题)并将其压缩成字典

标签: python beautifulsoup


【解决方案1】:

据我所知,您的方法是正确的,但执行不在那里。为了简单起见,让我们把它分解成几个小步骤:​​

  1. 获取你要抓取的表

  2. 如果是表,则将表的每一行作为列表获取

  3. 一旦我们有了行,我们将获取每个单独的单元格并将其放入字典中

    from bs4 import BeautifulSoup
    import requests
    
    main_data = {}
    URL = 'https://shropshire.gov.uk/waste/binday/index.jsc?p=0&go=Go&designation=3&postcode=sy3+9jt&gobutton=Go'
    content = requests.get(URL)
    
    soup = BeautifulSoup(content.text, 'html.parser')
    
    table = soup.find("table")
    rows = table.find_all("tr", {"class": ""})
    
    for row in rows:
        data_list = row.find_all("td")
        for data in data_list:
            is_valid = data.find("div")
            if is_valid:
                tags = [tag.text.strip() for tag in data.find_all("span")]
                date = data.find("div").text.strip()
                main_data[date] = tags
    
    print(main_data)
    

输出:

{'1': [], '2': [], '3': [], '4': ['Rubbish'], '5': [], '6': [], '7': [], '8': [], '9': [], '10': [], '11': ['Garden waste', 'Recycling boxes'], '12': [], '13': [], '14': [], '15': [], '16': [], '17': [], '18': ['Rubbish'], '19': [], '20': [], '21': [], '22': [], '23': [], '24': [], '25': ['Garden waste', 'Recycling boxes'], '26': [], '27': [], '28': [], '29': [], '30': []}

我希望你理解我所做的,你可以继续这个并在它之上增加以改进或使其更稳定

【讨论】:

  • 超级有用,谢谢!我想我想在我能走路之前就跑了,但一切都很好,可以让大脑继续运转
【解决方案2】:

<div class="maxi calendar_month"> 包含一个 HTML <table>,该表包含行 (<tr>),每行包含列 (td)。每列包含两个相邻的 div,第一个仅包含表示月份中的日期的数字,第二个 div 看起来像这样:<div class="a_collection"><div class="two_collection">,具体取决于给定中是否有一行或多行据我所知,细胞。第二个 div 出现在所有单元格中,即使是那些没有任何内容的单元格,例如“垃圾”或“花园垃圾”。但是,如果存在此类内容,则此 div 包含其他一些 div,并且您要查找的文本位于其中一个中。这是我想出的:

def main():

    from bs4 import BeautifulSoup
    import requests

    url = "https://shropshire.gov.uk/waste/binday/index.jsc?p=0&go=Go&designation=3&postcode=sy3+9jt&gobutton=Go"

    response = requests.get(url)
    response.raise_for_status()

    soup = BeautifulSoup(response.content, "html.parser")

    def get_table_as_dict(table):
        date_div = table.select_one("tr.month")
        month = date_div.select_one("th").find(text=True).strip().lower()
        year = date_div.select_one("span.year").get_text(strip=True)
        yield "month", month
        yield "year", year

        def get_days():
            for cell in table.select("td:not(.empty)"):
                day_number = cell.select_one("div:nth-child(1)").get_text(strip=True)
                content_divs = cell.select_one("div:nth-child(2) ").select("div:not([class*=\"print\"])")
                if content_divs:
                    content = ", ".join(text for text in [content_div.find(text=True).strip() for content_div in content_divs])
                else:
                    content = ""
                yield day_number, content
        yield "days", dict(get_days())

    for table in soup.findAll("table"):
        print(dict(get_table_as_dict(table)))

    return 0


if __name__ == "__main__":
    import sys
    sys.exit(main())

输出:

{'month': 'june', 'year': '2020', 'days': {'1': '', '2': '', '3': '', '4': 'Rubbish', '5': '', '6': '', '7': '', '8': '', '9': '', '10': '', '11': 'Garden waste, Recycling boxes', '12': '', '13': '', '14': '', '15': '', '16': '', '17': '', '18': 'Rubbish', '19': '', '20': '', '21': '', '22': '', '23': '', '24': '', '25': 'Garden waste, Recycling boxes', '26': '', '27': '', '28': '', '29': '', '30': ''}}
{'month': 'july', 'year': '2020', 'days': {'2': 'Rubbish', '9': 'Garden waste, Recycling boxes', '16': 'Rubbish', '23': 'Garden waste, Recycling boxes', '30': 'Rubbish'}}
{'month': 'august', 'year': '2020', 'days': {'6': 'Garden waste, Recycling boxes', '13': 'Rubbish', '20': 'Garden waste, Recycling boxes', '27': 'Rubbish'}}
>>> 

get_table_as_dict 是一个生成键值对的生成器,以便稍后您可以使用并折叠生成器以创建整个字典。我们在一个循环中执行此操作,该循环遍历 HTML 中的所有表,因此最后我们有三个字典,一个用于页面上的每个日历表/月。

get_days 也是一个生成键值对的生成器。它在get_table_as_dict 的第三个也是最后一个收益中产生其所有内容。基本上,我们使用 CSS 选择器遍历当前表中的所有单元格 - 遍历所有类不是“空”的 td 元素(表有时有 tds 和 class="empty" 来填充表在一个月的最后一天之后有更多的单元格,而这些单元格是我们不想要的。我们没有指定我们想要的单元格,而是指定我们不想要的单元格,因为我们想要的是那些在工作日登陆的单元格,没有课,周末登陆的单元格有class="weekend")。

一旦我们有了当前的有效单元格,我们就会得到第一个直接子 div,并且只获取它包含的直接文本,然后将其剥离 - 这是作为字符串的日期编号。

然后,我们创建一个content_divs 列表。这些代表了单元格中可能存在的潜在 div。有些单元格没有内容,有些单元格中有一个相关项(例如,“垃圾”),有些 div 中甚至有两个相关项(“花园垃圾”和“回收箱”)。我们通过选择当前单元格的第二个 div 子项来获取这些 div,并从该 div 中选择所有其类不包含子字符串“print”的子 div(无论出于何种原因,这些烦人的冗余 div 都存在,但我们没有对它们感兴趣)。

【讨论】:

  • 真的很有帮助,非常感谢!我猜想只限于当前月份表并不像从“ for table in soup.findAll("table"): "?
  • @AdamDavies 很高兴它有帮助。删除“全部”不会削减它,但如果你只想要第一张桌子,它仍然是一个非常简单的改变。一种方法是在循环体中添加一个break 语句,以便循环在第一次迭代后过早终止——不过,更有意义的是完全摆脱循环,并且简单地说print(dict(get_table_as_dict(soup.find("table"))))
  • 我会再玩一些,但这是一个了不起的开始,谢谢。该项目的最终目标是根据明天的垃圾箱熄灭情况点亮一盏智能灯 - 锁定无聊重创!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-03
  • 1970-01-01
  • 1970-01-01
  • 2016-08-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多