Python - BeautifulSoup - 选择带有“class”属性的“div”会显示html中的每个div

Mxc*_*Fhn 3 python beautifulsoup web-crawler

我正在尝试使用 BeautifulSoup 抓取 coinmarketcap.com (我知道有一个 API,出于培训目的,我想使用 BeautifulSoup)。到目前为止爬取的每一条信息都非常容易选择,但现在我喜欢让“持有者统计信息”看起来像这样:

持有者统计

我用于选择包含所需信息的特定 div 的测试代码如下所示:

import requests
from bs4 import BeautifulSoup

url = 'https://coinmarketcap.com/currencies/bitcoin/holders/'
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
holders = soup.select('div', class_='n0m7sa-0 kkBhMM')
print(holders)
Run Code Online (Sandbox Code Playgroud)

print(holders)的输出不是 div 的预期内容,而是网站的整个 html 内容。我附加了一张图片,因为输出代码太长。

输出代码

有谁知道,为什么会这样?

chi*_*n88 6

.select()当你想用作 css 选择器时应该使用。在这种情况下,holders = soup.select('div', class_='n0m7sa-0 kkBhMM')类部分基本上被忽略......并且它找到<div>任何类的所有部分。要指定特定的类,请使用.find_all(), 或更改您的.select()

holders = soup.select('div.n0m7sa-0.kkBhMM')

或者

holders = soup.find_all('div', class_='n0m7sa-0 kkBhMM')

现在,在这两种情况下,它将返回None一个空列表。这是因为该 class 属性不在源 html 中。该站点是动态的,因此这些类是在初始请求后生成的。所以你要么需要先使用Selenium渲染页面,然后拉取html,要么看看是否有api可以直接获取数据源。

有一个api可以获取数据:

import requests
import pandas as pd

alpha = ['count', 'ratio']
payload = {
'id': '1',
'range': '7d'}


for each in alpha:
        url = f'https://api.coinmarketcap.com/data-api/v3/cryptocurrency/detail/holders/{each}'
        jsonData = requests.get(url, params=payload).json()['data']['points']
        
        if each == 'count':
            count_df = pd.DataFrame.from_dict(jsonData,orient='index')
            count_df = count_df.rename(columns={0:'Total Addresses'})
            
        else:
            ratio_df = pd.DataFrame.from_dict(jsonData,orient='index')
            df = count_df.merge(ratio_df, how='left', left_index=True, right_index=True)
            
df = df.sort_index()          
        
Run Code Online (Sandbox Code Playgroud)

输出:

print(df.to_string())
                      Total Addresses  topTenHolderRatio  topTwentyHolderRatio  topFiftyHolderRatio  topHundredHolderRatio
2021-11-24T00:00:00Z         39279627               5.25                  7.19                10.51                  13.26
2021-11-25T00:00:00Z         39255811               5.25                  7.19                10.49                  13.22
2021-11-26T00:00:00Z         39339840               5.25                  7.19                10.51                  13.24
2021-11-27T00:00:00Z         39391849               5.23                  7.11                10.45                  13.18
2021-11-28T00:00:00Z         39505340               5.24                  7.11                10.45                  13.18
2021-11-29T00:00:00Z         39502099               5.24                  7.11                10.43                  13.16
2021-11-30T00:00:00Z         39523000               5.24                  7.11                10.38                  13.12
Run Code Online (Sandbox Code Playgroud)

您的其他选项是数据位于<script>json 格式的标签内。S0,您也可以通过这种方式从初始请求站点中取出它:

from bs4 import BeautifulSoup
import requests
import json
import re

url = 'https://coinmarketcap.com/currencies/bitcoin/holders/'
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')

jsonStr = str(soup.find('script', {'id':'__NEXT_DATA__'}))
jsonStr = re.search(r"({.*})", jsonStr).groups()[0]
jsonData = json.loads(jsonStr)['props']['initialProps']['pageProps']['info']['holders']

df = pd.DataFrame(jsonData).drop('holderList', axis=1).drop_duplicates()
Run Code Online (Sandbox Code Playgroud)

输出:

print(df.to_string())
   holderCount  dailyActive  topTenHolderRatio  topTwentyHolderRatio  topFiftyHolderRatio  topHundredHolderRatio
0     39523000       963625               5.24                  7.11                10.38                  13.12
Run Code Online (Sandbox Code Playgroud)

对于项目信息中的社交统计信息,它位于特定的 api 中:

import requests
import pandas as pd

url = 'https://api.coinmarketcap.com/data-api/v3/project-info/detail?slug=bitcoin'
jsonData = requests.get(url).json()
socialStats = jsonData['data']['socialStats']

row = {}
for k, v in socialStats.items():
    if type(v) == dict:
        row.update(v)
    else:
        row.update({k:v})
        
df = pd.DataFrame([row])
Run Code Online (Sandbox Code Playgroud)

输出:

print(df.to_string())
   cryptoId commits contributors  stars  forks watchers              lastCommitAt  members               updatedTime
0         1   31588          836  59687  30692     3881  2021-11-30T00:09:02.000Z  3617460  2021-11-30T16:00:02.365Z
Run Code Online (Sandbox Code Playgroud)