How to extract tables from websites in Python
There is a table. My goal is to extract the table and save it to a csv file. I wrote a code:
I lost from here. Anyone who can help on this? Thanks!
6 Answers 6
Pandas can do this right out of the box, saving you from having to parse the html yourself. read_html() extracts all tables from your html and puts them in a list of dataframes. to_csv() can be used to convert each dataframe to a csv file. For the web page in your example, the relevant table is the last one, which is why I used df_list[-1] in the code below.
It’s simple enough to do in one line, if you prefer:
P.S. Just make sure you have lxml , html5lib , and BeautifulSoup4 packages installed in advance.
So essentially you want to parse out html file to get elements out of it. You can use BeautifulSoup or lxml for this task.
You already have solutions using BeautifulSoup . I’ll post a solution using lxml :
I would recommend BeautifulSoup as it has the most functionality. I modified a table parser that I found online that can extract all tables from a webpage, as long as there are no nested tables. Some of the code is specific to the problem I was trying to solve, but it should be pretty easy to modify for your usage. Here is the pastbin link.
You could use it as follows:
The code above is an outline, but if you use the table parser from the pastbin link you should be able to get to where you want to go.
You need to parse the table into an internal data structure and then output it in CSV form.
Use BeautifulSoup to parse the table. This question is about how to do that (the accepted answer uses version 3.0.8 which is out of date by now, but you can still use it, or convert the instructions to work with BeautifulSoup version 4).
Once you have the table in a data structure (probably a list of lists in this case) you can write it out with csv.write.
Look at BeautifulSOup module. In documentation you will find many examples of parsing html.
Also for csv you have ready solution — csv module.
It should be quite easy.
Look at this answer parsing table with BeautifulSoup and write in text file. Also use google with next words «python beautifulsoup»
-
The Overflow Blog
Linked
Related
Hot Network Questions
Subscribe to RSS
To subscribe to this RSS feed, copy and paste this URL into your RSS reader.
Site design / logo © 2023 Stack Exchange Inc; user contributions licensed under CC BY-SA . rev 2023.3.11.43300
By clicking “Accept all cookies”, you agree Stack Exchange can store cookies on your device and disclose information in accordance with our Cookie Policy.
Parsing HTML Tables in Python with BeautifulSoup and pandas
Something that seems daunting at first when switching from R to Python is replacing all the ready-made functions R has. For example, R has a nice CSV reader out of the box. Python users will eventually find pandas, but what about other R libraries like their HTML Table Reader from the xml package? That’s very helpful for scraping web pages, but in Python it might take a little more work. So in this post, we’re going to write a brief but robust HTML table parser.
Writing a Table Scraper
Our parser is going to be built on top of the Python package BeautifulSoup. It’s a convenient package and easy to use. Our use will focus on the “find_all” function, but before we start parsing, you need to understand the basics of HTML terminology.
An HTML object consists of a few fundamental pieces: a tag. The format that defines a tag is
and it could have attributes which consistes of a property and a value. A tag we are interested in is the table tag, which defined a table in a website. This table tag has many elements. An element is a component of the page which typically contains content. For a table in HTML, they consist of rows designated by elements within the tr tags, and then column content inside the td tags. A typical example is
It turns out that most sites keep data you’d like to scrape in tables, and so we’re going to learn to parse them.
Parsing a Table in BeautifulSoup
To parse the table, we are going to use the Python library BeautifulSoup. It constructs a tree from the HTML and gives you an API to access different elements of the webpage.
Let’s say we already have our table object returned from BeautifulSoup. To parse the table, we’d like to grab a row, take the data from its columns, and then move on to the next row ad nauseam. In the next bit of code, we define a website that is simply the HTML for a table. We load it into BeautifulSoup and parse it, returning a pandas data frame of the contents.
0 | 1 | |
---|---|---|
0 | Hello! | Table |
As you can see, we grab all the tr elements from the table, followed by grabbing the td elements one at a time. We use the “get_text()” method from the td element (called a column in each iteration) and put it into our python object representing a table (it will eventually be a pandas dataframe).
Using Requests to Access a Web Content
Now, that we have our plan to parse a table, we probably need to figure out how to get to that point. That’s actually easier! We’re going to use the requests package in Python.
The Parser Object
So, now we can define our HTML table parser object. You’ll notice we added more bells and whistles to the html table parser. To summarize the functionality outside of basic parsing:
The tuples we return are in the form (table id, parsed table) for every table in the document.
Usage Example
Let’s do an example where we scrape a table from a website. We initialize the parser object and grab the table using our code above:
Rank | Player | Team | Points | Games | Avg | |
---|---|---|---|---|---|---|
0 | 1 | Cam Newton | CAR | 389.1 | 16 | 24.3 |
1 | 2 | Tom Brady | NE | 343.7 | 16 | 21.5 |
2 | 3 | Russell Wilson | SEA | 336.4 | 16 | 21.0 |
3 | 4 | Blake Bortles | JAC | 316.1 | 16 | 19.8 |
4 | 5 | Carson Palmer | ARI | 309.2 | 16 | 19.3 |
If you had looked at the URL above, you’d have seen that we were parsing QB stats from the 2015 season off of FantasyPros.com. Our data has been prepared in such a way that we can immediately start an analysis.
Final Thoughts
As you can see, this code may find it’s way into some scraper scripts once Football season starts again, but it’s perfectly capable of scraping any page with an HTML table. The code actually will scrape every table on a page, and you can just select the one you want from the resulting list. Happy scraping!
Web Scraping с помощью python
Недавно заглянув на КиноПоиск, я обнаружила, что за долгие годы успела оставить более 1000 оценок и подумала, что было бы интересно поисследовать эти данные подробнее: менялись ли мои вкусы в кино с течением времени? есть ли годовая/недельная сезонность в активности? коррелируют ли мои оценки с рейтингом КиноПоиска, IMDb или кинокритиков?
Но прежде чем анализировать и строить красивые графики, нужно получить данные. К сожалению, многие сервисы (и КиноПоиск не исключение) не имеют публичного API, так что, приходится засучить рукава и парсить html-страницы. Именно о том, как скачать и распарсить web-cайт, я и хочу рассказать в этой статье.
В первую очередь статья предназначена для тех, кто всегда хотел разобраться с Web Scrapping, но не доходили руки или не знал с чего начать.
Off-topic: к слову, Новый Кинопоиск под капотом использует запросы, которые возвращают данные об оценках в виде JSON, так что, задача могла быть решена и другим путем.
Задача
- Этап 1: выгрузить и сохранить html-страницы
- Этап 2: распарсить html в удобный для дальнейшего анализа формат (csv, json, pandas dataframe etc.)
Инструменты
Регулярные выражения, конечно, нам пригодятся, но использовать только их, на мой взгляд, слишком хардкорный путь, и они немного не для этого. Были придуманы более удобные инструменты для разбора html, так что перейдем к ним. , lxml
Это две наиболее популярные библиотеки для парсинга html и выбор одной из них, скорее, обусловлен личными предпочтениями. Более того, эти библиотеки тесно переплелись: BeautifulSoup стал использовать lxml в качестве внутреннего парсера для ускорения, а в lxml был добавлен модуль soupparser. Подробнее про плюсы и минусы этих библиотек можно почитать в обсуждении. Для сравнения подходов я буду парсить данные с помощью BeautifulSoup и используя XPath селекторы в модуле lxml.html.
Это уже не просто библиотека, а целый open-source framework для получения данных с веб-страниц. В нем есть множество полезных функций: асинхронные запросы, возможность использовать XPath и CSS селекторы для обработки данных, удобная работа с кодировками и многое другое (подробнее можно почитать тут). Если бы моя задача была не разовой выгрузкой, а production процессом, то я бы выбрала его. В текущей постановке это overkill.
Загрузка данных
Первая попытка
Приступим к выгрузке данных. Для начала, попробуем просто получить страницу по url и сохранить в локальный файл.
Открываем полученный файл и видим, что все не так просто: сайт распознал в нас робота и не спешит показывать данные.
Разберемся, как работает браузер
Однако, у браузера отлично получается получать информацию с сайта. Посмотрим, как именно он отправляет запрос. Для этого воспользуемся панелью «Сеть» в «Инструментах разработчика» в браузере (я использую для этого Firebug), обычно нужный нам запрос — самый продолжительный.
Как мы видим, браузер также передает в headers UserAgent, cookie и еще ряд параметров. Для начала попробуем просто передать в header корректный UserAgent.
На этот раз все получилось, теперь нам отдаются нужные данные. Стоит отметить, что иногда сайт также проверяет корректность cookie, в таком случае помогут sessions в библиотеке Requests.
Скачаем все оценки
Теперь мы умеем сохранять одну страницу с оценками. Но обычно у пользователя достаточно много оценок и нужно проитерироваться по всем страницам. Интересующий нас номер страницы легко передать непосредственно в url. Остается только вопрос: «Как понять сколько всего страниц с оценками?» Я решила эту проблему следующим образом: если указать слишком большой номер страницы, то нам вернется вот такая страница без таблицы с фильмами. Таким образом мы можем итерироваться по страницам до тех, пор пока находится блок с оценками фильмов ( <div > ).
Парсинг
Немного про XPath
XPath — это язык запросов к xml и xhtml документов. Мы будем использовать XPath селекторы при работе с библиотекой lxml (документация). Рассмотрим небольшой пример работы с XPath
Подробнее про синтаксис XPath также можно почитать на W3Schools.
Вернемся к нашей задаче
Теперь перейдем непосредственно к получению данных из html. Проще всего понять как устроена html-страница используя функцию «Инспектировать элемент» в браузере. В данном случае все довольно просто: вся таблица с оценками заключена в теге <div > . Выделим эту ноду:
Каждый фильм представлен как <div > или <div > . Рассмотрим, как вытащить русское название фильма и ссылку на страницу фильма (также узнаем, как получить текст и значение атрибута).
Еще небольшой хинт для debug’a: для того, чтобы посмотреть, что внутри выбранной ноды в BeautifulSoup можно просто распечатать ее, а в lxml воспользоваться функцией tostring() модуля etree.
Резюме
В результате, мы научились парсить web-сайты, познакомились с библиотеками Requests, BeautifulSoup и lxml, а также получили пригодные для дальнейшего анализа данные о просмотренных фильмах на КиноПоиске.
Полный код проекта можно найти на github’e.
Читаем таблицы веб-сайтов с помощью Pandas
Данные для Data Science проектов можно получать ото всюду, в том числе и с веб-сайтов, например, страниц Википедии. Сегодня мы расскажем, как извлечь все таблицы из веб-страницы с помощью функции read_html Python-библиотеки Pandas, а также обработать полученные данные, включая нормализацию и приведение типов.
Как работает парсинг сайтов
В Pandas есть функция — read_html, которая использует одну из библиотек для парсинга веб-страниц: BeautifulSoup4, html5lib или lxml. По умолчанию в Pandas стоит lxml, однако, в случае ее отсутствия будет применяться другая. Поэтому для корректного выполнения хотя бы одна из них должна быть установлена. Установить lxml можно, выполнив следующую операцию в командной строке:
pip install lxml
Одна из перечисленных Python-библиотек ищет на указанной веб-странице все таблицы под тэгом . Внутри таблицы могут быть заголовки и сами данные под тэгами и . В результате, Pandas-функция read_html ищет все таблицы на сайте и возвращает их в виде списка в формате DataFrame.
Не всегда все таблицы получается получить в приемлемом виде: могут быть проблемы с заголовками, типами данных, кодировкой. Поэтому прежде всего их необходимо будет обработать. Мы покажем, как в Python получить таблицы с Википедии со страницы пандемии COVID-19 и обработать их.
Извлечение таблиц
Вызовем функцию read_html, передав аргументом ссылку на страницу. Ниже приведён код в Python. Всего библиотека lxml нашла 17 таблиц.
import pandas as pd
tables = pd.read_html( ‘https://ru.wikipedia.org/wiki/%D0%9F%D0%B0%D0%BD%D0%B4%D0%B5%D0%BC%D0%B8%D1%8F_COVID-19')
Выберем на странице таблицу со статистикой заболеваний по странам и территориям. Поскольку искать её среди 17 таблиц утомительно, мы воспользуемся регулярными выражениями. Для этого передадим аргумент match с подходящим регулярным выражением, например, «стран». Код на Python выглядит следующим образом:
tables = pd.read_html( ‘https://ru.wikipedia.org/wiki/%D0%9F%D0%B0%D0%BD%D0%B4%D0%B5%D0%BC%D0%B8%D1%8F_COVID-19', match=’стран’)
Статистика заболеваний по странам и территориям
Всего нашлось 3 таблицы, которые содержат в своем заголовке слово «стран». Нужная нам находится под индексом 1. Однако таблица выглядит не лучшим образом: появился ещё один столбец, заполненный только NaN, название одного из столбцов содержит HTML-код, возможно нам не требуется результирующий заголовок на 2 уровне и ещё много чего. Исправим это.
Обрабатываем таблицы
В первую очередь избавимся от лишнего столбца, вызвав метод drop. Ещё мы удалим последние две строчки под номером 254 и 255, так как они содержат примечания, а не данные. Ниже представлен код на Python. Поскольку у нас двухуровневый заголовок, то и в аргументе он указывается в виде кортежа (tuple).
df.drop((‘Страны и территории’, ‘Всего 000000000000000’), axis=1, inplace=True) df.drop(axis=0, index=[254, 255], inplace=True)
Теперь отбросим нижний результирующий уровень, вызвав метод droplevel, а после этого переименуем нечитаемое название одного из столбцов в нормальное с помощью метода rename. В Python это выглядит следующим образом:
Кроме того, следует убрать источники, заключённые в квадратные скобки. Для этого мы воспользуемся методом replace, указав регулярное выражение и regex=True. Теперь таблица выглядит более приемлемо.
df.replace(<‘\[[0–9]+\]’: ‘’>, regex=True, inplace=True)
Обработанная таблица Нормализация и указание типов
Часто типом таблиц после парсинга веб-страниц является строка (str), которая в DataFrame указывается как object. Кроме того, может быть указана неизвестная кодировка. Это также следует исправить.
Исходная таблица включает подразделы: непризнанные государства, морские суда и т.д. Каждый подраздел имеет заголовок «Справочно» или «Морские суда». Мы воспользуемся этой информацией и разделим DataFrame. Прежде всего определим индексы этих заголовков и в цикле будем делить DataFrame на части, причем мы добавляем к индексу 1 каждый раз, чтобы не включать сам заголовок. Вот так выглядит Python-код:
indices = df[
(df[‘Страны и территории’].str.contains(‘Справочно’) |
df[‘Страны и территории’].str.contains(‘Морские суда’))
].index
dfs = []
cur = 0
for idx in indices:
if idx != indices[-1]:
part = df.iloc[cur:idx, :]
else:
part = df[idx+1: ]
cur = idx + 1
dfs.append(part)
Строки могут содержать нечитаемые символы, поэтому их следует нормализовать. Мы нормализуем по типу NFKC. После нормализации следует привести к соответствующему типу данных, например, float32. Но прежде всего нужно избавиться от нечисловых символов, а пробелы и «н/д» нужно заменить на Nan. В итоге, для одного из DataFrame код на Python имеет следующий вид:
temp_df = dfs[0]
temp_df.columns = temp_df.columns.str.normalize(‘NFKC’)
cols = temp_df.columns.drop(temp_df.columns[0]) # Выкинуть “Страны и территории”
for col in cols:
temp_df.loc[:, col] = temp_df.loc[:, col].str.normalize(‘NFKC’)
temp_df.loc[:, col] = temp_df.loc[:, col].replace(
<‘ ‘: np.nan, ‘н/д’: np.nan>, regex=True
).astype(‘float32’)
Весь приведенный код можно посмотреть в репозитории на Github.
А о том, как парсить сайты и обрабатывать полученные данные в Pandas на практических примерах Data Science, вы узнаете на нашем специализированном курсе по Python «DPREP: Подготовка данных для Data Mining на Python» в лицензированном учебном центре обучения и повышения квалификации IT-специалистов в Москве.