Недавно я работал с парсером для клиента, которому нужно было получить около миллиона записей с сайта недвижимости. После определенной отметки, парсер перестал работать по той причине, что я забыл внедрить определенные проверки, так как я думал, что клиент не пойдет в этом направлении, но он пошел!
Через несколько дней я задумался над написанием парсера в Python при помощи Beautifulsoup. В этой статье я хочу обсудить, как сделать ваш парсер более удобным для людей, не особо знакомых с технической частью.
Есть вопросы по Python?
На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!
Паблик VK
Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!
1. Проверьте код состояния 200
Всегда хорошо проверять код состояния HTTP заранее и делать это периодически. Вот хороший пример:
1 2 3 4 5 6 7 8 9 |
import requests r = requests.get('https://google.com') if r.status_code == 200: print('Все в норме!') if r.status_code == 404: print('Страница не существует!') |
2. Никогда не верьте HTML
Да, особенно если вы не можете его контролировать. Веб скрепинг зависит от HTML DOM, простое изменение в элементе или классе может сломать весь скрипт. Лучший способ справится с этим – узнать, возвращает ли None или нет.
1 2 3 4 5 |
page_count = soup.select('.pager-pages > li > a') if page_count: # Все в норме, работаем дальше... else: # Ошибка! Отправляем уведомление админу. |
Здесь я проверяю, вернул ли CSS селектор что-нибудь законное, если да – то продолжаем дальше.
3. Настройте заголовки
Python Requests не заставляет вас использовать заголовки при отправке запросов, однако есть несколько умных сайтов, которые не дадут вам прочитать ничего важного, если определенные заголовки не присутствуют.
Однажды я столкнулся с ситуацией: HTML, который я видел в браузере отличался от того, который был в моем скрипте. Так что делать запросы настолько правильными, насколько вы можете – очень хорошая практика. Меньшее, что вы должны сделать – это установить User-Agent.
1 2 3 4 5 |
headers = { 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36' } r = requests.get(url, headers=headers, timeout=5) |
4. Настройка таймаута
Одна из проблем Python Requests – это то, что вы не указываете таймаут, так что он будет ждать ответа от сайта до последнего. Это может быть хорошо при определенных условиях, но не в большинстве случаев. Тем не менее, всегда хорошо настроить значение таймаута для каждого запроса. Здесь я установлю таймаут на 5 секунд.
1 |
r = requests.get(url, headers=headers, timeout=5) |
5. Обработка ошибок
Всегда хорошо реализовать обработку ошибок. Это не только поможет избежать неожиданного выхода скрипта, но также помоет вести журнал ошибок и уведомлений. Используя запросы Python, я предпочитаю ловить ошибки следующим образом:
Попробуйте:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
try: # Логика нашего парсера. r = requests.get('https://python-scripts.com') except requests.ConnectionError as e: print("OOPS!! Connection Error. Make sure you are connected to Internet. Technical Details given below.\n") print(str(e)) except requests.Timeout as e: print("OOPS!! Timeout Error") print(str(e)) except requests.RequestException as e: print("OOPS!! General Error") print(str(e)) except KeyboardInterrupt: print("Someone closed the program") |
Проверьте последнюю часть кода. В ней программе указывается, что если кто-нибудь хочет завершить программу через Ctrl+C, то содержимое сначала оборачивается, после чего выполняется. Эта ситуация хороша, если вы храните информацию в файле и хотите сбросить все в момент выхода.
6. Эффективная обработка файлов
Одна из функций парсера – это хранение данных как в базе данных, так и в обычных файлах, таких как CSV/Text. Если собираете большой объем данных, это не означает, что операция ввода-вывода будет в цикле. Давайте рассмотрим, как это делается.
Пробуем:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
try: a_list_variable = [] a_list_variable.extend(a_func_return_record()) except requests.ConnectionError as e: print("Упс!! Ошибка подключения к интернету.") print(str(e)) except requests.Timeout as e: print("Упс!! Время ожидания истекло.") print(str(e)) except requests.RequestException as e: print("Упс!! Возникла непредвиденная ошибка!") print(str(e)) except KeyboardInterrupt: print("Кто-то закрыл принудительно программу.") finally: print("Total Records = " + str(len(property_urls))) try: # файл для хранения URL record_file = open('records_file.txt', 'a+') record_file.write("\n".join(property_urls)) record_file.close() except Exception as ex: print("Возникла ошибка при сохранении данных, текст ошибки:") print(str(e)) |
Здесь я вызываю функцию (хотя вы не обязаны делать то же самое), которая добавляет записи в список. Как только это будет сделано, или программа будет остановлена, перед завершением она просто сохранит весь список в файл за раз. Намного лучше, чем несколько операций ввода-вывода
Надеюсь, эта статья была для вас полезной. Пожалуйста, Поделитесь своим опытом о том, как сделать парсер более эффективным!
Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.
E-mail: vasile.buldumac@ati.utm.md
Образование
Universitatea Tehnică a Moldovei (utm.md)
- 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»