【问题标题】:Using Regex + BeautifulSoup to scrape XML and store to Pandas使用 Regex + BeautifulSoup 抓取 XML 并存储到 Pandas
【发布时间】:2020-08-25 21:55:12
【问题描述】:

我正在使用 beautifulSoup 抓取一些 xml 网站,然后将抓取的数据存储到数据帧中。 XML 通常是一致的格式,因此抓取工作正常。但可能有 15% 的时间,数据不会保存到数据框中,因为其中一个前缀略有不同。

例如,在抓取这三个 URL 时,第二个和第三个 URL 会毫无问题地存储到数据帧中,而第一个则不会。

from bs4 import BeautifulSoup
import requests
import pandas as pd

session = requests.Session()

# urls to loop through
form_urls = ['https://www.sec.gov/Archives/edgar/data/1418814/000141881220000017/vac13f021420.xml',
             'https://www.sec.gov/Archives/edgar/data/820124/000095012320003895/408.xml',
             'https://www.sec.gov/Archives/edgar/data/1067983/000095012320002466/form13fInfoTable.xml']

# Create dataframe and set columns to match XML doc
cols = ['nameOfIssuer', 'titleOfClass', 'cusip', 'value', 'sshPrnamt',
        'sshPrnamtType', 'putCall', 'investmentDiscretion',
        'otherManager', 'Sole', 'Shared', 'None']

res_df = pd.DataFrame(columns=cols)


# Iterate over URLs
for form_url in form_urls:
    data = []
    soup = BeautifulSoup(session.get(form_url).content, 'lxml')
    print(soup)

    for info_table in soup.find_all(['ns1:infotable', 'infotable']):
        row = []
        for col in cols:
            d = info_table.find([col.lower(), 'ns1:' + col.lower()])
            row.append(d.text.strip() if d else 'NaN')
        data.append(row)
    url_df = pd.DataFrame(data, columns=cols)
    res_df = res_df.append(url_df, ignore_index=True)

print(res_df)

那么,如果前缀采用意外格式(例如,可能是空字符串或其他大小写字母和数字的组合),如何使刮板更加灵活?

【问题讨论】:

    标签: python regex pandas beautifulsoup


    【解决方案1】:

    您提供的第一个链接的第二行是 n1:infoTable 而不是 ns1:infoTable,因此要使您的代码正常工作,您需要考虑到这一点。

    from bs4 import BeautifulSoup
    import requests
    import pandas as pd
    import re
    
    
    session = requests.Session()
    
    # urls to loop through
    form_urls = ['https://www.sec.gov/Archives/edgar/data/1418814/000141881220000017/vac13f021420.xml',
                 'https://www.sec.gov/Archives/edgar/data/820124/000095012320003895/408.xml',
                 'https://www.sec.gov/Archives/edgar/data/1067983/000095012320002466/form13fInfoTable.xml']
    
    # Create dataframe and set columns to match XML doc
    cols = ['nameOfIssuer', 'titleOfClass', 'cusip', 'value', 'sshPrnamt',
            'sshPrnamtType', 'putCall', 'investmentDiscretion',
            'otherManager', 'Sole', 'Shared', 'None']
    
    res_df = pd.DataFrame(columns=cols)
    
    
    # Iterate over URLs
    for form_url in form_urls:
        data = []
        soup = BeautifulSoup(session.get(form_url).content, 'lxml')
    
        for info_table in soup.find_all(re.compile("([A-Za-z0-9]+:|)infotable")):
            row = []
            for col in cols:
                pattern = re.compile("([A-Za-z0-9]+:|)" + col.lower())
                d = info_table.find(pattern)
                row.append(d.text.strip() if d else 'NaN')
            data.append(row)
        url_df = pd.DataFrame(data, columns=cols)
        res_df = res_df.append(url_df, ignore_index=True)
    

    编辑:现在前缀可以不存在(空字符串'')或者它可以是小写,大写字母和数字的组合

    【讨论】:

    • 这是否适用于行以n1:infoTable— like ns1:infoTable, n:infoTable` 等开头的所有排列?我想每个 uRL 的变化可能会略有不同,所以我需要尽可能灵活。
    • 如果一个 URL 只有一个前缀(如 n1 或 ns1 或无),那么是的,它将适用于所有 URL,您只需在 form_urls 列表中设置前缀。如果不是这种情况(一个 URL 可以在不同的行中有多个前缀,例如 n1 和 ns1),您可以尝试将前缀列表而不是一个前缀添加到 form_urls 中,然后在该行之后用代码中的汤迭代该前缀列表。
    • 我收到ValueError: too many values to unpack (expected 2)
    • 好吧,如果你在 form_url 中添加了前缀列表,那么 for 循环是相同的 for form_url, prefixes in form_urls:,然后在下面添加 for prefix in prefixes,其他一切都应该保持不变。
    • 嗯,我明白了,但我并不总是知道每个 URL 的前缀,我担心将它添加到同一个列表可能会导致问题。所以我想我需要一个不同的解决方案。
    【解决方案2】:

    在第一个 form_url 中有“n1:...”标签。 这就是你有空列表的原因。

    你可以试试:

    from bs4 import BeautifulSoup
    import requests
    import pandas as pd
    
    session = requests.Session()
    
    # urls to loop through
    form_urls = ['https://www.sec.gov/Archives/edgar/data/1418814/000141881220000017/vac13f021420.xml',
                 'https://www.sec.gov/Archives/edgar/data/820124/000095012320003895/408.xml',
                 'https://www.sec.gov/Archives/edgar/data/1067983/000095012320002466/form13fInfoTable.xml']
    
    # Create dataframe and set columns to match XML doc
    cols = ['nameOfIssuer', 'titleOfClass', 'cusip', 'value', 'sshPrnamt',
            'sshPrnamtType', 'putCall', 'investmentDiscretion',
            'otherManager', 'Sole', 'Shared', 'None',]
    
    res_df = pd.DataFrame(columns=cols)
    
    
    # Iterate over URLs
    for form_url in form_urls:
        data = []
        soup = BeautifulSoup(session.get(form_url).content, 'lxml')
        for info_table in soup.find_all(['n1:infotable', 'infotable']):
            row = []
            for col in cols:
                if form_url == form_urls[0]:
                    col = "n1:" + col
                d = info_table.find([col.lower(), 'ns1:' + col.lower()])
                row.append(d.text.strip() if d else 'NaN')
            # print(row)
            data.append(row)
        print(data)
        print("==============================================")
        url_df = pd.DataFrame(data, columns=cols)
        res_df = res_df.append(url_df, ignore_index=True)
    
    print(res_df)
    

    【讨论】:

    • 谢谢。是否有可能使它更灵活,因为我担心infotable 的前缀可能有其他变体,我可能没有事先列出的列表。
    猜你喜欢
    • 1970-01-01
    • 2021-02-28
    • 1970-01-01
    • 2023-03-09
    • 1970-01-01
    • 2018-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多