О парсинге веба
Я не занимаюсь этим регулярно. Раз в 4-6 месяцев мне требуется распарсить какую-нибудь страничку или насобирать данных. Решил собрать мысли о том, как делать это легко в одну кучу.
Я использую для этих целей python3.6, requests и BeautifulSoup - и они неплохи.
Я используйте многоуровневую систему парсинга, о которой и пойдёт речь.
Сбор сырых данных -> разбор сырых данных -> подчистка данных -> использование данных.
1. Сперва пишем минимальную итерацию по нужным объектам.
for item in soup.find_all('blabla'):
print(item)
exit(1)
Добавляйте дебаговые принты, передвигайте exit после первого же разобранного элемента.
2. Затем добавляем кэширование.
Это нужно по двум причинам:
- собирать таким образом данные, ну, не самое приличное дело
- сетевые запросы - дело долгое и если вам нужно больше 40 страниц, лучше их закэшировать локально
Сделать это можно создав враппер вокруг requests:
endpoint = 'https://blabla.bla/'
def get(link):
""" caching request """
path = link.replace(endpoint, '.')
if not os.path.exists(path):
directory = os.path.dirname(path)
if not os.path.exists(directory):
os.makedirs(directory)
with open(path, 'w') as fd:
fd.write(requests.get(link).content.decode('utf-8'))
with open(path) as fd:
return fd.read()
Само собой обычные текстовые файлы - не обязательно, можете пихать это в редис, можете записывать на бумажку. Суть в том, что потом вы поменяете парсер и вам не надо будет заново DoSить сайт с данными своими дурацкими запросами. Инвалидация такого кэша делается легко - просто удаляем весь кэш к чёрту.
И только затем добавляем складывание данных в машинно-читаемый вид. Более того, я обычно делю это на две части.
3. Структурирование данных
Просто парсю страницу, создаю dict в который кладу то, что смог выдернуть из страницы как есть. Что не смог достать - заменяю дефолтными (заведомо плохими) значениями. Иногда для удобства формирование такого дикта удобно положить в класс объекта. Зачем - можно добавить строковое представление для отладки (__str__
) чтобы просто print(Blah(link))
, легче декомпозировать пухнущую функцию parse()
.
class Blah(object):
def __init__(self, link):
self.data = self.parse(link)
def parse(self):
raw_data = get(link)
soup = BeautifulSoup(raw_data, "html.parser")
return {
'xx': soup.xx,
'yy': soup.yy,
}
def __str__(self):
json.dumps(self.data, indent=2)
4. Очистка данных
Приведение данных в нормальную форму. “3,20GHz” превращаются в 3,20 чтобы мы могли сортировать и фильтровать это после импорта в табличный процессор.
Чем дополнить статью в будущем
- Упрощение дебага добавлянием click для передачи аргументов и создание удобного cli