В моей серии постов будет описан мой путь от Junior'a до Middle'a, а потом возможно даже до Senior'a. Программировать будем на Python.
P.S. Документация к некоторым малоизвестным библиотекам будет прикреплена в конце.
Кратко о себе: Python начал изучать два года назад, особых продвижений не было.
Разочарованием стали сами основы, поскольку учил я их полтора года. Сейчас же углубленно изучаю язык, понимаю его структуру и как все происходит. В следующих статьях буду выкладывать код, использую все новые, и новые библиотеки, а их как вы знаете у питона очень много :)
С чего бы начать? Пару месяцев назад я написал свой первый парсер. Оказалось, что писать парсеры довольно просто и на них даже можно зарабатывать. Пока что покажу пару примеров, используя стек из bs4 + requests. Парсить будем наш любимый Хабр.
#для начала импортируем нужные нам модули
from bs4 import BeautifulSoup as bs
import requests
from fake_useragent import UserAgent
Не уверен, что все знакомы с библиотекой fake_useragent. Довольно удобна для парсинга, создает фейк user-agent'a.
ua = UserAgent()
headers = {'accept': '*/*', 'user-agent': ua.firefox}
Это следующий небольшой блок нашего кода. В первой строке, мы создали переменную ua, которая использует методы класса UserAgent. Во второй строке, мы создали словарь, который в будущем поможет нам при парсинге.
Теперь создаем саму функцию для парсинга.
def without_post(url, headers):
response = requests.get(url, headers=headers)
if response.status_code == 200:
soup = bs(response.text, 'html.parser')
links = {}
for i in soup.find_all('a', {'class': 'post__title_link'}):
links.update({i.text: i.get('href')})
return links
else:
print("Connection Error")
Данная функция будет парсить ссылку, которую мы укажем на наличие тега с классом «post__title_link».
Самый простой способ сохранить данные — сохранить их в файл. Так и сделаем.
url = "https://habr.com/ru/all/"
links = without_post(url, headers)
with open('parsed.txt', 'w') as f_obj:
for name, href in links.items():
f_obj.write(name + ':\n' + href + '\n\n')
В конце получаем файл, в котором записаны названия и ссылки постов, которые мы собрали с первой страницы.
Исходный(полный) код, только уже без комментариев:
from bs4 import BeautifulSoup as bs
from fake_useragent import UserAgent
import requests
ua = UserAgent()
headers = {'accept': '*/*', 'user-agent': ua.firefox}
def without_post(url, headers):
response = requests.get(url, headers=headers)
if response.status_code == 200:
soup = bs(response.text, 'html.parser')
links = {}
for i in soup.find_all('a', {'class': 'post__title_link'}):
links.update({i.text: i.get('href')})
return links
else:
print("Connection Error")
url = "https://habr.com/ru/all/"
links = without_post(url, headers)
with open('parsed.txt', 'w') as f_obj:
for name, href in links.items():
f_obj.write(name + ':\n' + href + '\n\n')
Хороший результат! Если не считать пропусков строк и комментариев, то мы уложились ровно в 20 строк. Для начала довольно-таки хорошо :)
Как и обещал, ссылки на документации использованных библиотек:
Requests: *жмяк*
bs4: *жмяк*
fake_useragent: *жмяк*
Всем спасибо за внимание! До встреч!
P.S. Если будет какой-либо фидбек, то следующая статейка не заставит себя ждать
Комментарии (29)
tabbols95
28.12.2019 19:55Что-то очень плохо для начала. Ожидал большего. Просто собрать пару ссылок и записать его в txt, карл, даже не в csv.
LazyTalent
28.12.2019 20:01bs4 ~в 10 раз медленнее, чем lxml
requests, чтобы парсер работал еще быстрее, то лучше использовать Session.
session = requests.Session() r = session.get('http://some.url')
fake_useragent — полезный инструмент, но необходимо использовать только последнию версию (иначе может поломаться). И, наверное, вместо конкретного браузера лучше использовать ua.random ("# and the best one, random via real world browser usage statistic")just_another_user
30.12.2019 11:35Для одного запроса разве будет преимущество в скорости от использования Session?
shep
28.12.2019 22:49+1Откройте пожалуйста для себя итераторы, try...except, lxml, logging. Форматирование строк в стиле php, попробуйте f'...'. Определитесь какие вы хотите использовать кавычки для строк и делайте единообразно. Узнайте про локальную и глобальную области видимости. Вот после этого уже будет ближе к джуну.
Попробуйте писать в PyCharm и внимательно анализируйте все предупреждения — он будет неким судьёй вашему коду.
Хотя бы вручную тестируйре код на разные вариации. Если хабр не вернёт http200, то Вы получите ни о чем не говорящую ошибку, создадите пустой файл и после этого скрипт вылетит с исключением.
Krau5 Автор
28.12.2019 23:46Спасибо, прочитаю, выучу. Думаю следующая статья будет уже намного лучше по качеству кода.
P.S работаю в VS Code, потому что PyCharm как-то не зашел.
CaerDarrow
28.12.2019 23:42А какая разница сколько строк? Питон выразительный язык, но зачем каждый раз писать сколько строк программа?)
soymiguel
29.12.2019 00:56Ситуация «когда джун и дилетант не синонимы».
Впечатлил даже не суп в 2019 году, а это:
ua = UserAgent() headers = {'accept': '*/*', 'user-agent': ua.firefox}
Вы прикидываетесь реальным браузером с acceptом */*. Какую глубокую цель вы преследуете этим двумя строками, пусть даже не обращая внимания на то, что requests и так выставляет Accept: */* по умолчанию?Krau5 Автор
29.12.2019 01:36Не знал, что Accept: "/" — по умолчанию.
Реальным браузером прикидываюсь потому, что при строке:
response = requests.get(url)
Мне выкидывало ConnectionError или что-то на подобии этого.
kbaa
29.12.2019 01:18статья-демонстрация того, что автор смог. так-то можно было и код не выкладывать, а просто написать что-то типа «ребята, я нашел пару прикольных питонячих библиотек и с ними парсить сайты проще, чем регулярками, всё ништяк, начинаю путь в миддлы»
хочется пожелать автору 2 вещи — успешного профессионального роста (без сарказма, тут я добрый) и обдумывать, несет ли хоть какую-то пользу для окружающих подобного рода «статья» (а вот тут я злой — потому что на запрос «python scraping» гугл выдает кучу материала по requests и bs)Krau5 Автор
29.12.2019 01:32Спасибо, за пожелания. На счет пользы статьи. Во время своих первых попыток парсинга я не понимал что я делаю, в то время я даже основы ООП не выучил. К сожалению про lxml я не знал, а Scrapy стал для меня слишком сложным, потому решил сделать парсер на bs4 + reuqests, зная, что хабр, кроме опытных программистов читают такие же джуны, как и я.
kbaa
29.12.2019 02:00вот в этом и дело. лично я, например, когда ищу что-то, проклинаю авторов статей, в которых весь материал сводится к описанию того, как они использовали пару стандартных приемов. порой много времени уходит только на то, чтобы найти нормальную публикацию по интересующему вопросу. а уж конкретно связку bs4 + requests видел даже добавляя «java» в запрос.
если не секрет, сколько времени и/или выполненных задач прошло от «первых попыток парсинга» до «решил сделать парсер на bs4 + requests»? просто у меня в свое время на подобное (только на java) ушло пару вечеров (1й вечер поиск популярных решений, 2й вечер — чтение доков к выбранной библиотеке)Krau5 Автор
29.12.2019 10:26Когда я проходил книгу Эрика Метиза, то уже тогда был зареган на фрилансе. Часто видел, что люди заказывали парсеры и за довольно большую цену. Подмечу, что на тот момент по книге я даже до классов не дошел. В результате, на то чтобы понять что такое парсер и как его писать ушло около 2-3 месяцев.
dom1n1k
29.12.2019 14:37В моей серии постов будет описан мой путь от Junior'a до Middle'a
Не совсем понятно, в какой части пути вы сейчас находитесь? Это анонс будущих статей или ретроспектива? И если первое, то на какие ориентировочно календарные сроки рассчитана серия?Krau5 Автор
29.12.2019 21:40Скорее анонс будущих статей. По срокам, думаю что год, возможно меньше, возможно больше.
Тут уже как выйдет.
fougasse
А обработка ошибок где?
Если файл не открылся, например. Почему код только 200?
Как-то не очень на миддла, если честно.
Krau5 Автор
Я не миддл, вроде писал, что вместе с вами буду описывать свой путь от Юниора до Миддла
Извините, если ошибся)
Maksclub
Anshi85
Ошибки обрабатывать конечно нужно всем) но не каждый обжигается на этом,
я работаю джуниор разработчиком полгода, стек Python/Django, поначалу конечно тоже не использовал try/exception, обычно пихал везде logger.info() вместо print, чтобы в случае чего понять где ошибка, потом понял что так жить нельзя, так как моим кодом будут пользоваться люди, я то ладно в логе посмотрю, что там поломалось, а они как поймут? Начал заворачивать все в if/else, но где то на 2 месяц работы понял что это тоже не дело и начал использовать try/exception внезапно оказалось очень удобно, мой сервис не падает и мне сухо и спокойно)
Я подозреваю, что автор пишет в основном для себя или для одного клиента, какие-то разовые заказы, поэтому и не сталкивался с проблемами, когда сервисом пользуется много людей и есть интеграция со сторонними сервисами и очень важно, чтобы все действия в сервисе были максимально информативны ну и нужна какая никакая отказоустойчивость сервиса в плане возникновения ошибок или сбоя в коде.
Krau5 Автор
Раньше писал для себя, учил, работал на фрилансе с какими-либо минимальными заказами и использовал правило Главное, чтобы работало. Сейчас же приходится переучиватся, вспоминать PEP8, ибо когда передаешь код получаешь тонну критику в сторону качества.
Anshi85
Вы главное не воспринимайте критику близко к сердцу, а постарайтесь сделать для себя выводы и уроки на будущее. Главное не унывать и стремиться к совершенству, я знаете единственный разработчик на проекте был полгода, учиться особо не у кого было, сперва писал как мог, а потом через пару дней узнав что то новое переписывал все что писал до этого. Помните каждый синьор — помидор был когда то как мы джуниором) Желаю вам да и всем пользователям хабра в новом году здоровья и меньше багов.