Очистка таблицы: ошибка IndexError: индекс списка вне диапазона

#python #jupyter-notebook #screen-scraping

#python #jupyter-записная книжка #очистка экрана

Вопрос:

Я новичок в python. Я использую его в jupyter notebooks для очистки таблицы из Википедии. Весь код, который я написал, работает, за исключением случаев, когда я хочу поместить информацию в файл csv. Появляется ошибка «Индекс списка индексов вне диапазона».

Вот код:

 url = 'https://en.wikipedia.org/wiki/List_of_countries_by_population_(United_Nations)'

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

s = requests.Session()
response = s.get(url, timeout=10)
response

table_id = 'main'

response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
print(soup.prettify().encode('UTF-8'))

table = soup.find('table', attrs={'id': table_id})
for row in table.find_all('tr'):
    print(row)

table = soup.find('table', attrs={'id': table_id})
for row in table.find_all('tr')[1:]:
    col = row.find_all('td')
    print(col[0].find('a').contents[0]) 
    print(col[1].string) #name
    print(col[2].string)
    print(col[3].string)
    print(col[4].string)
    print(col[5].find(text=True))

csvfile = open('population.csv', 'w')
csvwriter = csv.writer(csvfile, delimiter=',')

headers = ('COUNTRY','CONTINENT','SUBREGION', 'POPULATION_2018', 'POPULATION_2019', 'CHANGE')
csvwriter.writerow(headers)

table = soup.find('table', attrs={'id': table_id})
for row in table.find_all('tr')[1:]:
    col = row.find_all('td')
    country = col[0].find('a').contents[0]
    continent = col[1].string
    subregion = col[2].string
    population_2018 = col[3].string
    population_2019 = col[4].string
    change = col[5].find(text=True)
    
    parsed_row = (country, continent, subregion, population_2018, population_2019, change)

    csvwriter.writerow(parsed_row)

csvfile.close()
  

Большое вам спасибо!

Ответ №1:

У меня есть ответы на две части. Самый простой способ выполнить вашу задачу и где в вашем коде ошибка.

Позвольте pandas обрабатывать requests BeautifulSoup и csv для вас.

 import pandas as pd

URI = 'https://en.wikipedia.org/wiki/List_of_countries_by_population_(United_Nations)'
df = pd.read_html(URI)[3]
df.to_csv('population.csv', index=False)
  

pandas .read_html возвращает список всех таблиц на веб-странице. Ваша таблица была с индексом 3. С этим я ее сохранил .to_csv .

С .read_html помощью вы можете передавать атрибуты определенной таблицы, например attrs = {'id': 'table'}

 # the table is now at index 0
df = pd.read_html(URI, attrs={'id':'main'})[0]
  

Вы также можете указать анализатор, который будет использоваться BeautifulSoup, .read_html вызывающий:

 df = pd.read_html(URI, attrs={'id':'main'}, flavor='lxml')[0] 
# 'lxml' is known for speed. But you can use `html.parser` if `lxml` or `html5lib` are not installed.
  

См . Дополнительную документацию .read_html

Обновление: отладка вашего кода

Ошибка в вашем коде связана с пустой таблицей. использование условий if решает проблему:

 url = 'https://en.wikipedia.org/wiki/List_of_countries_by_population_(United_Nations)'

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

s = requests.Session()
response = s.get(url, timeout=10)
response

table_id = 'main'

response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
#print(soup.prettify().encode('UTF-8'))

csvfile = open('population.csv', 'w')
csvwriter = csv.writer(csvfile, delimiter=',')

headers = ('COUNTRY','CONTINENT','SUBREGION', 'POPULATION_2018', 'POPULATION_2019', 'CHANGE')
csvwriter.writerow(headers)

table = soup.find('table', attrs={'id': table_id})
for row in table.find_all('tr')[1:]:
    col = row.find_all('td')
    # this is all that was missing
    if col:
        country = col[0].find('a')['title']
        continent = col[1].string
        subregion = col[2].string
        population_2018 = col[3].string
        population_2019 = col[4].string
        change = col[5].find(text=True)
    
    parsed_row = (country, continent, subregion, population_2018, population_2019, change)
    csvwriter.writerow(parsed_row)

csvfile.close()
  

Комментарии:

1. Большое вам спасибо. Prayson, я попробую второе решение, о котором вы упомянули. Это выглядит проще, поскольку оно короче. Еще раз спасибо!

Ответ №2:

Прайсон В. Дэниел уже дал ответ, и я предлагаю другой способ.

 import requests
from simplified_scrapy import SimplifiedDoc, utils, req
url = 'https://en.wikipedia.org/wiki/List_of_countries_by_population_(United_Nations)'

s = requests.Session()
res = s.get(url, timeout=10)

rows = []
headers = ('COUNTRY','CONTINENT','SUBREGION', 'POPULATION_2018', 'POPULATION_2019', 'CHANGE')
rows.append(headers)

table_id = 'main'

doc = SimplifiedDoc(res.text)
table = doc.select('table#' table_id) # Get the table by id.

trs = table.tbody.children.children[1:] # Get all data rows
for tr in trs:
  row = [tr[0].a.text] # First col, get first link
  row.extend(tr.text[1:]) # Left cols
  rows.append(row)

utils.save2csv('test_wiki.csv', rows) # Save data to csv