为什么CSV在标头后变成空白行?

时间:2018-07-11 03:26:27

标签: python html beautifulsoup

我正在使用BeautifulSoup从网站上抓取表格,但是对于为什么在标题后打印空白行以及如何解决它感到困惑。

我的代码是:

page = requests.get('http://racing-reference.info/loopdata/2018-18/W')
page.encoding = 'utf-8'
soup = BeautifulSoup(page.text, 'html.parser')

table = soup.find_all(class_ = 'tb')
headers = [th.text for th in table[2].select("tr th")]

with open("out.csv", "w", newline='') as f:
    wr = csv.writer(f)
    wr.writerow(headers)
    wr.writerows([[td.text 
                   for td in row.find_all("td")] 
                   for row in table[2].select("tr + tr")])

正在读取的HTML如下:

<BR><BR><TABLE class=tb WIDTH=100% CELLPADDING=3 CELLSPACING=0>
<TR><TD ALIGN=CENTER COLSPAN=19 class=col>
<TR><TD COLSPAN=19 HEIGHT=20 ALIGN=CENTER class=newhead>Loop data for this race:</TD></TR>
<TR><TH class=col><A HREF=/loopdata?s=1&series=W&id=2018-18>Driver</A></TH>
<TH class=col><A HREF=/loopdata?s=2&series=W&id=2018-18>Start</A></TH>
<TH class=col><A HREF=/loopdata?s=3&series=W&id=2018-18>Mid Race</A></TH>
<TH class=col><A HREF=/loopdata?s=4&series=W&id=2018-18>Finish</A></TH>
<TH class=col><A HREF=/loopdata?s=5&series=W&id=2018-18>High Pos.</A></TH>
<TH class=col><A HREF=/loopdata?s=6&series=W&id=2018-18>Low Pos.</A></TH>
<TH class=col><A HREF=/loopdata?s=7&series=W&id=2018-18>Avg. Pos.</A></TH>
<TH class=col><A HREF=/loopdata?s=8&series=W&id=2018-18>Pass Diff.</A></TH>
<TH class=col><A HREF=/loopdata?s=9&series=W&id=2018-18>Green Flag Passes</A></TH>
<TH class=col><A HREF=/loopdata?s=10&series=W&id=2018-18>Green Flag Times Passed</A></TH>
<TH class=col><A HREF=/loopdata?s=11&series=W&id=2018-18>Quality Passes</A></TH>
<TH class=col><A HREF=/loopdata?s=12&series=W&id=2018-18>Pct. Quality Passes</A></TH>
<TH class=col><A HREF=/loopdata?s=13&series=W&id=2018-18>Fastest Lap</A></TH>
<TH class=col><A HREF=/loopdata?s=14&series=W&id=2018-18>Top 15 Laps</A></TH>
<TH class=col><A HREF=/loopdata?s=15&series=W&id=2018-18>Pct. Top 15 Laps</A></TH>
<TH class=col><A HREF=/loopdata?s=16&series=W&id=2018-18>Laps Led</A></TH>
<TH class=col><A HREF=/loopdata?s=17&series=W&id=2018-18>Pct. Laps Led</A></TH>
<TH class=col><A HREF=/loopdata?s=18&series=W&id=2018-18>Total Laps</A></TH>
<TH class=col><A HREF=/loopdata?s=19&series=W&id=2018-18>DRIVER RATING</A></TH>
</TR>
<TR CLASS=odd><TD class=col NOWRAP><A HREF=/driverlog/joneser02/W/2018 title="View this driver's loop data for all races">Erik Jones</A></TD><TD class=col ALIGN=RIGHT>29</TD><TD class=col ALIGN=RIGHT>26</TD><TD class=col ALIGN=RIGHT>1</TD><TD class=col ALIGN=RIGHT>1</TD><TD class=col ALIGN=RIGHT>31</TD><TD class=col ALIGN=RIGHT>18</TD><TD class=col ALIGN=RIGHT>31</TD><TD class=col ALIGN=RIGHT>153</TD><TD class=col ALIGN=RIGHT>122</TD><TD class=col ALIGN=RIGHT>46</TD><TD class=col ALIGN=RIGHT>30.1</TD><TD class=col ALIGN=RIGHT>9</TD><TD class=col ALIGN=RIGHT>49</TD><TD class=col ALIGN=RIGHT>29.2</TD><TD class=col ALIGN=RIGHT>1</TD><TD class=col ALIGN=RIGHT>0.6</TD><TD class=col ALIGN=RIGHT>168</TD><TD class=col ALIGN=RIGHT>84.6</TD></TR>

标题和数据可以正常打印,但是标题和数据之间有空白行,我似乎无法摆脱。我试过在行上使用any()函数,但这没有用。

谢谢。

1 个答案:

答案 0 :(得分:1)

在我看来,这是来自:

[[td.text for td in row.find_all("td")] for row in table[2].select("tr + tr") if row]

如果我们看一下您的HTML,它的一般形式为:

<table>
  <tr><td></td></tr>
  <tr><td></td></tr>
  <!-- note this tr has no tds -->
  <tr><th></th><!-- ... --></tr>
  <tr><td></td><!-- ... --></tr>

请注意,您从中获取标题的行是<tr>,其后紧跟<tr>(与tr + tr相匹配)。但是,由于其中没有<td>,因此列表理解为空:[td.text for td in row.find_all("td")]

代替这种方法,我建议以下内容:

  1. 收集表中的所有<tr>rows = table.select('tr')
  2. 从该列表的顶部删除行,直到找到标题(其中有多个<th>的行)
  3. 然后,既然标题已被剥离,则可以对其余行使用列表推导来提取数据:[[td.text for td in row] for row in rows]

或者,如果您不需要对数据进行任何处理(而只想将其直接转换为CSV),则只需进行一次列表解析即可提取<th>或{{ 1}} s。

<td>