使用嵌套SPAN标签抓取多行TD

时间:2020-07-31 19:41:27

标签: python web-scraping beautifulsoup

长话短说,昨天才发现BeautifulSoup,多年来一直没有做任何形式的脚本或编码,在时间紧迫的情况下,乞求帮助。 :)

我的最终目标是使用垂直样式数据表抓取一系列网页并将其拖放到CSV。借助古老的Google,以及今天早些时候我的第一篇关于堆栈溢出的文章(至少十年或以上的第一次),我掌握了基础知识。我可以输入带有URL列表的文本文件,标识包含我需要的表的DIV,刮擦表,以便第一列成为我的标题,第二列成为数据行,并重复下一个URL(不重复标题) )。我遇到的问题是这些页面的代码比我想象的要糟糕得多,其中包括大量额外的行,额外的空格,以及现在我发现的,标签内的嵌套标签,其中大多数是空的。但是,在跨度和多余的行之间,它使到目前为止的脚本忽略了TD内部的某些数据。有关丑陋的页面代码的示例:

                            <div id="One" class="collapse show" aria-labelledby="headingOne" data-parent="#accordionExample">
                                <div class="card-body">
                                    <table class="table table-borderless">

                                        <tbody>
                                            <tr>
                                                <td>ID:</td>
                                                <td>
                                                    096626 180012
                                                </td>
                                            </tr>
                                            <tr>
                                                <td>Address:</td>
                                                <td>
                                                    

                                                        
                                                            
                                                                
                                                                    
                                                                        
                                                                            
                                                                                
                                                                                            
                                                                                                        
                                                                                                                    1234  Main St
                                                </td>
                                            </tr>

                                            <tr>
                                                <td>Addr City:</td>
                                                <td>
                                                    City
                                                </td>
                                            </tr>                                      
                                            <tr>
                                                <td> Name :</td>
                                                <td>
                                                    Last name, first name<span> </span>
                                                        
                                                </td>
                                            </tr>

                                            
                                                    <tr>
                                                        <td>In Care Of Address:</td>
                                                        <td>
                                                            1234<span> </span>
                                                                <span> </span>
                                                                    Main<span> </span>
                                                                        St <span> </span>
                                                                             <span> </span>
                                                                                 <span> </span>
                                                                                    
                                                        </td>
                                                    </tr>
                                                    <tr>
                                                        <td>City/State/Zip:</td>
                                                        <td>
                                                            City<span> </span>
                                                                ST<span> </span>
                                                                    Zip<span>-</span>
                                                                        Zip+4
                                                        </td>
                                                    </tr>

                                        </tbody>

                                    </table>
                                </div>
                            </div>
 

到目前为止,我的代码是(现在,URL文本文件具有如上所述的本地存储的HTML文件的名称,但是已经用实际的URL进行了测试以验证该部分是否有效):

from bs4 import BeautifulSoup
import requests
import csv
import pandas as pd

contents = []
headers = []
values = []
rows = []
num = 0

with open('sampleurls.txt','r') as csvf:
    urls = csv.reader(csvf)
    for url in urls:
        contents.append(url)

for url in contents:
    html = open(url[0]).read()
    soup = BeautifulSoup(html, 'html.parser')
    trs = soup.select('div#One tr')
    for t in trs:
        for header, value in zip(t.select('td')[0], t.select('td')[1]):
            if num == 0:
                headers.append(' '.join(header.split()))
            values.append(' '.join(value.split()))
    rows.append(values)
    values = []
    num += 1

df = pd.DataFrame(rows, columns= headers)
print(df.head())

df.to_csv('output5.csv')

执行后,脚本似乎忽略换行符或跨度之后的所有内容,不确定是哪个。我得到的输出是:

,ID:,Address:,Addr City:,Name :,In Care Of Address:,City/State/Zip:
0,096626 180012,1234 Main St,City,"Last name, first name",1234,City

在“收据地址:”列中,我没有得到“ 1234 Main St”,而是得到了“ 1234”。我也尝试了不使用join / split函数的情况,并且地址的其余部分仍然被忽略。有没有解决的办法?从理论上讲,我不需要跨度内的任何数据,因为唯一填充的是zip + 4中的连字符,我不在乎。

请注意,我假设输出中的第一列是CSV编写功能的一部分,但是如果有一种摆脱它的方法,我想这样做。并不是很大,因为当我将CSV导入数据库时​​我可以忽略它,但是越干净越好。

1 个答案:

答案 0 :(得分:0)

在第一位发布正确的信息会更容易..:)

尝试:

from bs4 import BeautifulSoup
import pandas as pd
html = '''
<html>
 <body>
  <div aria-labelledby="headingOne" class="collapse show" data-parent="#accordionExample" id="One">
   <div class="card-body">
    <table class="table table-borderless">
     <tbody>
      <tr>
       <td>
        ID:
       </td>
       <td>
        096626 180012
       </td>
      </tr>
      <tr>
       <td>
        Address:
       </td>
       <td>
        1234  Main St
       </td>
      </tr>
      <tr>
       <td>
        Addr City:
       </td>
       <td>
        City
       </td>
      </tr>
      <tr>
       <td>
        Name :
       </td>
       <td>
        Last name, first name
        <span>
        </span>
       </td>
      </tr>
      <tr>
       <td>
        In Care Of Address:
       </td>
       <td>
        1234
        <span>
        </span>
        <span>
        </span>
        Main
        <span>
        </span>
        St
        <span>
        </span>
        <span>
        </span>
        <span>
        </span>
       </td>
      </tr>
      <tr>
       <td>
        City/State/Zip:
       </td>
       <td>
        City
        <span>
        </span>
        ST
        <span>
        </span>
        Zip
        <span>
         -
        </span>
        Zip+4
       </td>
      </tr>
     </tbody>
    </table>
   </div>
  </div>
 </body>
</html>
'''

contents = []
headers = []
values = []
rows = []
num = 0
soup = BeautifulSoup(html, 'html.parser')
trs = soup.select('div#One tr')
for t in trs:
    for header, value in zip(t.select('td')[0], t.select('td:nth-child(2)')):
        if num == 0:
            headers.append(' '.join(header.split()))
        values.append(value.get_text(' ', strip=True))
    rows.append(values)
df = pd.DataFrame(rows, columns= headers)
print(df.head())

df.to_csv('output5.csv')

希望它现在可以使用更多相关信息。

csv:

enter image description here