【问题标题】:Web scraping a webpage for nested table using BeautifulSoupWeb 使用 BeautifulSoup 为嵌套表抓取网页
【发布时间】:2018-01-23 18:01:30
【问题描述】:

我正在尝试从此页面获取一些信息:https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSM2437275

我对提取特征数据特别感兴趣的地方如下:

group_id: xxx
medicore_id: xxxxxxx
date_of_visit_sample_drawn_date: xxxxxxx
rin: xxxxxx
donor_id: xxxxx
sle_visit_designation: xxxxxxx
bold_shipment_batch: xxxxxx
rna_concentrated: xxxxxx
subject_type: xxxxxxx

以此类推。 在检查页面后,我意识到这些信息深深地嵌套在其他更大的表中,并且没有特殊的类/ID 可以让我有效地解析特征信息。 我一直没有成功尝试在表中查找表,但我发现有时并非所有表都被读取。这是我目前所拥有的:

from bs4 import BeautifulSoup
import requests

source= requests.get("https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?
acc=GSM2437275").text


soup = BeautifulSoup(source, 'lxml')
table = soup.find_all('table') 
for i in table:
  print i.prettify()
print (len(table)) #22 tables

print (table[6].prettify()) #narrow down on relevant table
table = table[6]

table_subtables = table.find_all('table')
for i in table_subtables:
   print (i.prettify())

print len(table_subtables) #14 tables

tbb = table_subtables[1] 

tbb_subtable = tbb.find_all('table')
for i in tbb_subtable:
  print (i.prettify())
print len(tbb_subtable) #12 tables

tbbb = tbb_subtable[5] 

    tbbb_subtable = tbbb.find_all('table')
for i in tbbb_subtable:
  print (i.prettify())
print len(tbbb_subtable) # 6 tables

以此类推。但是,当我继续这样做时,我发现并非所有表格都被读取。有人可以指出更好的解决方案吗?

【问题讨论】:

    标签: python beautifulsoup


    【解决方案1】:

    可以用正则表达式和urllib刮取数据,专门刮取关键字及其对应的值:

    import re
    import urllib 
    data = str(urllib.urlopen('https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSM2437275').read())
    target_vals = ['group_id', 'medicore_id', 'date_of_visit_sample_drawn_date', 'rin', 'donor_id', 'sle_visit_designation', 'bold_shipment_batch', 'rna_concentrated', 'subject_type']
    final_data = {i:re.findall('(?<={}:\s)\w+'.format(i), data)[0] for i in target_vals}
    

    输出:

    {
     'date_of_visit_sample_drawn_date': '2009', 
     'rna_concentrated': 'No', 
      'sle_visit_designation': 'Baseline', 
      'rin': '8', 
      'subject_type': 'Patient', 
      'donor_id': '19', 
      'bold_shipment_batch': '1', 
      'medicore_id': 'B0019V1', 
      'group_id': 'A'
    }
    

    编辑:给定多个链接,您可以从每个生成的数据中创建一个pandas 数据框:

    import re
    import urllib
    import pandas as pd
    def get_data_from_links(link, target_vals=['group_id', 'medicore_id', 'date_of_visit_sample_drawn_date', 'rin', 'donor_id', 'sle_visit_designation', 'bold_shipment_batch', 'rna_concentrated', 'subject_type']):
        data = str(urllib.urlopen(link).read())
        return {i:re.findall('(?<={}:\s)\w+'.format(i), data)[0] for i in target_vals}
    returned_data = get_data_from_links('https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSM2437275')
    df = pd.DataFrame([returned_data])
    

    输出:

      bold_shipment_batch date_of_visit_sample_drawn_date donor_id group_id  \
      0                   1                            2009       19        A   
    
      medicore_id rin rna_concentrated sle_visit_designation subject_type  
          0     B0019V1   8               No              Baseline      Patient 
    

    如果您有一个要从中检索数据的链接列表,您可以通过构建结果数据的嵌套字典来构建一个表以传递给DataFrame.from_dict

    link_lists = ['link1', 'link2', 'link3']
    final_data = {i:get_data_from_links(i) for i in link_lists}
    new_table = pd.DataFrame.from_dict(final_data, orient='index')
    

    输出(假设第一个链接是'https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSM2437275'):

           rin rna_concentrated date_of_visit_sample_drawn_date  \
    link1   8               No                            2009   
    
      sle_visit_designation bold_shipment_batch group_id subject_type  \
    link1              Baseline                   1        A      Patient   
    
      medicore_id donor_id  
    link1     B0019V1       19  
    

    【讨论】:

    • 非常感谢,这很有帮助!有没有办法可以将“date_of_visit_sample_drawn_date”等描述存储为列名和该列下的相应值?另外,如果我要提供几个指向可变数据的链接的向量,它会存储所有给定网页的这些列标题下的所有信息吗?
    • @NithishaKh 很高兴为您提供帮助!请查看我最近的编辑。
    【解决方案2】:

    Ajax1234 在他的解决方案中展示的方式绝对是最好的方式。但是,如果硬编码索引不是障碍,并且您希望避免使用正则表达式来达到同样的效果,那么这是您可能会考虑尝试的另一种方法:

    from bs4 import BeautifulSoup
    import requests
    
    res = requests.get("https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSM2437275")
    soup = BeautifulSoup(res.content, 'lxml')
    for items in soup.select("td[style*='justify']")[2:3]:
        data = '\n'.join([item for item in items.strings][:9])
        print(data)
    

    输出:

    group_id: A
    medicore_id: B0019V1
    date_of_visit_sample_drawn_date: 2009-09-14
    rin: 8.5
    donor_id: 19
    sle_visit_designation: Baseline
    bold_shipment_batch: 1
    rna_concentrated: No
    subject_type: Patient
    

    【讨论】:

    • 非常感谢,我一直在阅读有关使用 BeautifulSoup 的信息,因此拥有此替代解决方案非常有帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-17
    • 1970-01-01
    • 2019-04-10
    • 1970-01-01
    • 2014-08-16
    • 1970-01-01
    相关资源
    最近更新 更多