【问题标题】:How to scrape two tables and write to one csv?如何抓取两张表并写入一个csv?
【发布时间】:2020-08-16 10:57:00
【问题描述】:

我正在尝试抓取该网站上的两个表:https://www.nsw.gov.au/covid-19/latest-news-and-updates

在这个阶段,我无法获得初始输出。我的刮刀没有返回任何错误,所以我看不到问题。

理想情况下,我想将这两个表合并为一个,另外还有一个用于操作的列和一个用于表标题的值(示例如下)。

这是我尝试使用的代码:

from bs4 import BeautifulSoup
from requests import get
from csv import writer

url = 'https://www.nsw.gov.au/covid-19/latest-news-and-updates'

r = get(url)
soup = BeautifulSoup(r.text, 'lxml')


tables = soup.find_all('nsw-table-responsive')

for num, table in enumerate(tables, start=1):

    filename = 'covidstatus.csv' % num

    with open(filename, 'w') as f:

        data = []

        csv_writer = writer(f)

        rows = table.find_all('tr')
        for row in rows:

            headers = row.find_all('th')
            if headers:
                csv_writer.writerow([header.text.strip() for header in headers])

            columns = row.find_all('td')
            csv_writer.writerow([column.text.strip() for column in columns])

下面是我的理想输出示例

Location,Dates,Action
Glebe: Jambo Jambo African Restaurant,7pm to 10:30pm on Friday 31 July 2020,Self-isolate and get tested immediately
Hamilton: Bennett Hotel,5:30pm to 10pm on Friday 31 July,Self-isolate and get tested immediately
Bankstown: BBQ City Buffet,7pm to 8.30pm on Saturday 1 August,Monitor for symptoms
Broadmeadow: McDonald Jones Stadium,7:30pm to the end of the Newcastle Jets match on Sunday 2 August,Monitor for symptoms

感谢任何人为此提供的任何帮助。

【问题讨论】:

  • 如果你可以使用 pandas,pd.concat(pd.read_html('<url>')).to_csv('<csv_path>')

标签: python web-scraping beautifulsoup python-requests


【解决方案1】:

此脚本将数据保存到data.csv

import csv
import requests
from bs4 import BeautifulSoup


url = 'https://www.nsw.gov.au/covid-19/latest-news-and-updates'
soup = BeautifulSoup(requests.get(url).content, 'html.parser')

all_data = []
for row in soup.select('tr:has(td)'):
    all_data.append(
        [td.get_text(strip=True, separator='\n') for td in row.select('td')]
    )
    all_data[-1].append(row.find_previous('h4').text)
    all_data[-1][0] = all_data[-1][0].replace('\n', '')

with open('data.csv', 'w', newline='') as csvfile:
    csv_writer = csv.writer(csvfile, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
    for row in all_data:
        csv_writer.writerow(row)

来自 LibreOffice 的 data.csv 的屏幕截图:


编辑:(写标题):

...

with open('data.csv', 'w', newline='') as csvfile:
    csv_writer = csv.writer(csvfile, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
    csv_writer.writerow(['Location', 'Dates', 'Type'])
    for row in all_data:
        csv_writer.writerow(row)

【讨论】:

  • 太好了,谢谢。在列标题 (th) 中写最简单的方法是什么?
  • 谢谢!!我实际上想再添加一件事,但这可能很棘手。这些表经常更新。我想添加一个附加的“状态”列,其中包含“活动”或“非活动”作为捕获历史数据的值。因此,当从表中删除一行时,它会保留在 csv 输出中并获得“非活动值”。这样的事情能实现吗?
  • @krazykrejza 是的,这是可以实现的。您可以将 CSV 视为数据库(第四列“活动”):在开头加载 CSV,然后从网络加载数据并进行比较。将从 Web 加载的数据设置为活动,其他一切为非活动。保存 CSV。
【解决方案2】:

这是工作代码,如果您有任何问题,请告诉我

 from bs4 import BeautifulSoup
 from requests import get
 from csv import writer

 url = 'https://www.nsw.gov.au/covid-19/latest-news-and-updates'

 r = get(url)
 soup = BeautifulSoup(r.text, 'lxml')


 tables = soup.find_all('table')

 for num, table in enumerate(tables, start=1):

     filename = 'covidstatus.csv'


     with open(filename, 'w') as f:

         data = []

         csv_writer = writer(f)

         rows = table.find_all('tr')
         for row in rows:

             headers = row.find_all('th')
             if headers:
                 head = [header.text.strip() for header in headers]
                 print(head)
                 csv_writer.writerow([header.text.strip() for header in headers])

             columns = row.find_all('td')
             print([column.text.strip() for column in columns])
             csv_writer.writerow([column.text.strip() for column in columns])

这是输出

['Location', 'Dates']
[]
['Hamilton: Sydney Junction Hotel', '11pm on Saturday 1 August to 1:15am on Sunday 2 August']
['Huskisson: Wildginger', '7:45pm to 10:30pm on Saturday 8 August']
['Lidcombe: Dooleys Lidcombe Catholic Club', '5pm on Friday 7 August to 6:30am on Saturday 8 August\xa0\n\t\t\t4:30pm to 11:30pm on Saturday 8 August\n\t\t\t1pm to 9pm on Sunday 9 August\n\t\t\t12pm to 9:30pm on Monday 10 August\xa0\nIf you were at this venue for at least 1 hour during any of these
times, you must self-isolate and get tested and stay isolated for 14 days after your last day at the venue within these dates. (Advice updated 16\xa0August)']
['Mollymook: Rick Stein at Bannisters', '8pm to 10:30pm on Saturday 1 August for at least one hour\nSelf-isolate until midnight 15 August or until you have received a negative result, whichever is later.']
['New Lambton: Bar 88 - Wests New Lambton', '5pm to 7:15pm on Sunday 2 August']
['Newcastle: Hamilton to Adamstown Number 26 bus', '8:20am on Monday 3 August']
['Location', 'Dates']
[]
[]
['Bowral:\xa0Horderns Restaurant at Milton Park Country House Hotel and Spa', '7:45pm to 9:15pm on\xa0Sunday 2 August']
['Broadmeadow: McDonald Jones Stadium', '7:30pm to the end of the Newcastle Jets match on Sunday 2 August']
['Campbelltown: Bunnings Warehouse', '11am to 7pm on Tuesday 4 August\xa0\n\t\t\t8am to 4pm on Wednesday 5 August\n\t\t\t1pm to 3pm on Thursday 6 August']
['Castle Hill:\xa0Castle Towers Shopping Centre', '3:30pm to 5pm on Friday\xa07 August']
['Cherrybrook:\xa0PharmaSave Cherrybrook Pharmacy in Appletree Shopping Centre', '4pm to 7pm on Thursday 6 August']
['Concord:\xa0Crust Pizza', '4pm to\xa08pm on\xa0Thursday 6 August\n\t\t\t5pm to 9pm on\xa0Friday 7 August']
['Double Bay:\xa0Café Perons', '1pm to 2pm on\xa0Saturday 8 August']
['Liverpool:\xa0Liverpool Hospital', '7am to 3pm on Thursday 6 August\n\t\t\t7am to 3pm on Friday 7 August\n\t\t\t5am to 1:30pm on Saturday 8 August\n\t\t\t5am to 1:30pm on Sunday 9 August']
['Liverpool: Westfield Liverpool', '10:30am to 11am and 12:30pm to 1pm on Friday 7 August']
['Marrickville: Woolworths -\xa0Marrickville Metro Shopping Centre', '7pm to 7:20pm on Sunday 2 August']
['Parramatta: Westfield Parramatta', '4pm to 5:30pm on Wednesday\xa05 August\n\t\t\t12pm to 1pm on Saturday 8 August']
['Pennant Hills: St Agatha's', '6:30 am to 7am on\xa0Wednesday 5 August\n\t\t\t6:30 am to 7am on Thursday 6 August']
['Penrith: Baby Bunting', '1:15pm to 1:45pm on Saturday 8 August']
['Rhodes: IKEA', '1:20pm to 2:20pm on Saturday 8 August']
['Rose Bay:\xa0Den Sushi', '7:15pm to 8:45pm on\xa0Saturday 8 August']
['Smithfield:\xa0Chopstix Asian Cuisine, Smithfield RSL', 'Friday 31 July to Saturday 9 August']
['Wetherill Park: 5th Avenue Beauty Bar', '2pm to 3pm\xa0on Saturday 8 August']

In [81]:

【讨论】:

  • 这很有帮助。不幸的是,csv 输出与打印有很大不同。第一个表(从您发布的打印件中的第 3 行开始)根本不输出,并且有额外的字符(空格)创建空白行
  • 你能在这里分享你在 csv 中得到的输出吗?
【解决方案3】:

最简单的方法是使用Pandas 中的.read_html。 Pandas 会为你做requestsBeautifulSoup

import pandas as pd

URI = 'https://www.nsw.gov.au/covid-19/latest-news-and-updates'

# get tables
tables = pd.read_html(URI)

t1 = tables[0]
t2 = tables[1].dropna(axis=0)

# append tables
t = t1.append(t2, ignore_index=True)

# send tables to csv file
t.to_csv('my_table.csv', index=False, encoding='utf-8')

您可能必须安装 lxml, html5lib,因为 Pandas 的 .read_html 需要这些依赖项。

结果:

【讨论】:

  • 谢谢你,我要去了解更多关于熊猫的知识。我安装了两个库,但遇到了这个问题``` Double Bay:Café Perons 8 月 8 日星期六下午 1 点至下午 2 点利物浦:利物浦医院 8 月 6 日星期四上午 7 点至下午 3 点 8 月 7 日星期五上午 7 点至下午 3 点 上午 5 点至下午 1:30 8 月 8 日星期六上午 5 点至 8 月 9 日星期日下午 1:30 ```
  • 啊,这看起来像是.to_csv 中的编码问题。我会更新我的答案。如果您想事后处理数据或将其发送到不同的格式,如数据库、Hive 等,Pandas 真的很方便:)
  • 试试utf-8latin1。我会在 PC 上测试它们。顺便说一句,您使用的是哪个 Python 版本?
猜你喜欢
  • 1970-01-01
  • 2019-08-16
  • 2013-11-14
  • 2021-04-19
  • 2023-04-04
  • 1970-01-01
  • 2021-09-02
  • 2017-02-10
  • 2019-11-12
相关资源
最近更新 更多