【问题标题】:Scraping Excel File and read on the fly抓取 Excel 文件并即时读取
【发布时间】:2021-09-25 08:43:31
【问题描述】:

我正在尝试从网站 (https://rigcount.bakerhughes.com/na-rig-count) 获取 Excel 文件,将其下载并保存到内存中以便使用 Pandas 读取。该文件是一个 .xlsb,包含超过 700,000 行。

使用我正在使用的代码,我只能获得 1457 行...我尝试使用 chunksize 但它不起作用。

这是我的代码:

from bs4 import BeautifulSoup
import requests
import os
import pandas as pd
from io import BytesIO, StringIO 

url = "https://rigcount.bakerhughes.com/na-rig-count"
r = requests.get(url)
soup = BeautifulSoup(r.text, 'html.parser')
link_temp = soup.find_all('div', attrs={'class': 'file-link'})

for i in link_temp:
    if 'Rig Count Pivot Table' in i.find().text:
        link = i
        break
    
href = link.a.get('href')

response = requests.get(href)

#Store file in memory
f = BytesIO()

for line in response.iter_content():
    f.write(line)
    
f.seek(0)

pd.read_excel(f, engine='pyxlsb', sheet_name = 1, skiprows=0)

我尝试将其保存在本地并重新打开,但存在我无法解决的编码问题。

感谢您的帮助! :)

【问题讨论】:

    标签: python dataframe web-scraping beautifulsoup


    【解决方案1】:
    import trio
    import httpx
    from bs4 import BeautifulSoup
    import pandas as pd
    from functools import partial
    
    
    async def main(url):
        async with httpx.AsyncClient(timeout=None) as client:
            r = await client.get(url)
            soup = BeautifulSoup(r.text, 'lxml')
            tfile = soup.select_one('.file-link:-soup-contains(Table)').a['href']
            async with client.stream('GET', tfile) as r:
                fname = r.headers.get('content-disposition').split('=')[-1]
                async with await trio.open_file(fname, 'wb') as f:
                    async for chunk in r.aiter_bytes():
                        await f.write(chunk)
    
            df = await trio.to_thread.run_sync(partial(pd.read_excel, fname, sheet_name=3, engine="pyxlsb"))
            print(df)
    
    if __name__ == "__main__":
        trio.run(main, 'https://rigcount.bakerhughes.com/na-rig-count')
    

    输出:

                  Country      County        Basin DrillFor  ... Week RigCount State/Province  PublishDate
    0       UNITED STATES      SABINE  Haynesville      Gas  ...   13        1      LOUISIANA        40634    
    1       UNITED STATES  TERREBONNE        Other      Oil  ...   13        1      LOUISIANA        40634    
    2       UNITED STATES   VERMILION        Other      Gas  ...   13        1      LOUISIANA        40634    
    3       UNITED STATES   VERMILION        Other      Gas  ...   13        1      LOUISIANA        40634    
    4       UNITED STATES        EDDY      Permian      Oil  ...   13        1     NEW MEXICO        40634    
    ...               ...         ...          ...      ...  ...  ...      ...            ...          ...    
    769390  UNITED STATES        KERN        Other      Oil  ...   29        1     CALIFORNIA        44393    
    769391  UNITED STATES        KERN        Other      Oil  ...   29        1     CALIFORNIA        44393    
    769392  UNITED STATES        KERN        Other      Oil  ...   29        1     CALIFORNIA        44393    
    769393  UNITED STATES        KERN        Other      Oil  ...   29        1     CALIFORNIA        44393    
    769394  UNITED STATES        KERN        Other      Oil  ...   29        1     CALIFORNIA        44393    
    
    [769395 rows x 13 columns]
    
    >注意:您似乎遇到了 `pyxlsb` 阅读器中的错误。使用索引读取工作表是原因,但使用 `sheet_name='Master Data'` 可以正常工作。

    更新

    问题是excel文件有2个隐藏表,第2个表确实有1457行,主数据实际上是第4个表,所以sheet_name=3可以工作

    上次更新

    为了关注Python DRY Principle。我注意到我们不需要将文件保存在本地,甚至不需要将文件可视化并存储到内存中,然后将其加载到 pandas。

    实际上response的内容本身是存储在内存中的,所以我们可以通过将r.content直接传递给pandas来一次性加载!

    使用下面的代码:

    import trio
    import httpx
    from bs4 import BeautifulSoup
    import pandas as pd
    from functools import partial
    
    
    async def main(url):
        async with httpx.AsyncClient(timeout=None) as client:
            r = await client.get(url)
            soup = BeautifulSoup(r.text, 'lxml')
            tfile = soup.select_one('.file-link:-soup-contains(Table)').a['href']
            r = await client.get(tfile)
            df = await trio.to_thread.run_sync(partial(pd.read_excel, r.content, sheet_name=3, engine="pyxlsb"))
            print(df)
    
    if __name__ == "__main__":
        trio.run(main, 'https://rigcount.bakerhughes.com/na-rig-count')
    
    

    【讨论】:

    • 有趣。感谢您添加一些解释。
    • @QHarr 在调查了pyxlsb 之后,我注意到这不是一个错误。检查更新的答案。
    • 有实际的工作表名称吗?如果 3 是索引,那似乎比索引更稳健。 arg 是否允许传递字符串文字?
    • @QHarr 你可以通过sheet_name=3sheet_name="Master Data"阅读它
    • @QHarr 是的,但我会根据网站内的模式缩短它。
    猜你喜欢
    • 1970-01-01
    • 2015-08-18
    • 1970-01-01
    • 1970-01-01
    • 2020-10-07
    • 2016-04-23
    • 1970-01-01
    相关资源
    最近更新 更多