Хотелось бы сделать быстрое исправление, но вот переписывание с использованием итераторов (не знаю, удобны ли вы с ними - одна проблема при отладке, вам нужно преобразовать генератор в список, используя list(...)
).
Основная идея состоит в том, чтобы извлечь список списков строк из HTML, используя BeautifulSoup
, аналогично чтению CSV, а затем отфильтровать списки по мере необходимости.
Также хорошая идея дляотдельная логика синтаксического анализа (создание переменной output
ниже) и операция сохранения файла.Таким образом, код легче модифицировать и «рассуждать».
import re
import csv
import itertools
import requests
from bs4 import BeautifulSoup
def row_to_list(tr):
return list(map(lambda x: x.string, tr.children))
def is_separator(x):
try:
return x[0].startswith("-")
except AttributeError:
return False
def separate_by(xs, sep_func):
return [list(x[1]) for x in itertools.groupby(xs, sep_func) if not x[0]]
def is_representative(block):
return block[0][0].startswith("U. S. Representative District")
def num_district(headline):
return re.search("\d+", headline)[0]
def yield_entry(block):
"""
Args:
- *block* example:
[['U. S. Representative District 4 - ', None, None, None],
[None, 'John Ratcliffe(I)', 'REP', '188,667', '75.70%'],
[None, 'Catherine Krantz', 'DEM', '57,400', '23.03%'],
[None, 'Ken Ashby', 'LIB', '3,178', '1.28%'],
[None, None, None, '-----------', None],
[None, None, 'Race Total', '249,245\n', None]]
"""
district = num_district(block[0][0])
for line in block[1:-2]:
line[0] = district
line[3] = line[3].replace(",", "")
yield line
if __name__ == "__main__":
sos_2018_site = 'https://elections.sos.state.tx.us/elchist331_state.htm'
r = requests.get(sos_2018_site)
soup = BeautifulSoup(r.text, features="lxml")
table_rows = soup.find_all('tr')
table = [row_to_list(tr) for tr in table_rows]
blocks = separate_by(table, is_separator)
blocks_rep = filter(is_representative, blocks)
output = []
for br in blocks_rep:
for k in yield_entry(br):
print(k)
output.append(k)
with open('election2018.csv', 'w', newline='') as f:
writer = csv.writer(f)
writer.writerows(output)
PS После написания этого кода я думаю, что любая настоящая демократия должна сообщать о результатах выборов в JSON, а не в HTML.