Авторът на статията, чийто превод публикуваме днес, казва, че целта му е да разкаже за разработването на уеб скрапер на Python с използване на Selenium, който извършва търсене на цени за самолетни билети. При търсенето на билети се използват гъвкави дати (+- 3 дни в зависимост от зададените дати). Скраперът запазва резултатите от търсенето в Excel файл и изпраща на този, който го е стартирал, имейл с обща информация за намереното. Задачата на този проект е да помогне на пътешествениците да намерят най-изгодните предложения.
Ако, докато се запознавате с материала, усетите, че се губите — погледнете на статията.
Какво ще търсим?
Вие сте свободни да ползвате описаната тук система, както желаете. Аз, например, я използвах за търсене на уикенд турове и билети до родния ми град. Ако сериозно сте настроени да търсите изгодни билети — можете да стартирате скрипта на сървър (обикновен , за 130 лева на месец, напълно подходящ за това) и да настроите да се стартира един-два пъти на ден. Резултатите от търсенето ще пристигат на имейла ви. Освен това, препоръчвам да настроите всичко така, че скриптът да запазва Excel файл с резултатите от търсенето в папка Dropbox, което ще ви позволи да преглеждате подобни файлове от всякъде и по всяко време.

Все още не съм намерил тарифи с грешки, но предполагам, че е възможно
При търсене, както вече беше споменато, се използва "гъвкава дата", скриптът намира предложения, разположени в рамките на три дни от зададените дати. Въпреки че при стартиране на скрипта се извършва търсене само по една посока, не е трудно да се усъвършенства, за да може да събира данни по няколко посоки на полетите. С него можете дори да търсите грешни тарифи, подобни находки могат да се окажат доста интересни.
Защо е нужен още един уеб скрапер?
Когато за първи път се захванах с уеб скрейпинг, честно казано, не ми беше особено интересно. Исках да правя повече проекти в сферата на прогнозното моделиране, финансовия анализ и, може би, в областта на анализа на емоционалната окраска на текстовете. Но се оказа, че е много интересно — да се разбере как да се създаде програма, която събира данни от уеб сайтове. Докато навлизах в темата, разбрах, че именно уеб скрейпингът е „двигателят“ на интернет.
Може би ще решите, че това е твърде смело изказване. Но помислете за факта, че компания Google започна с уеб скрейпер, който Лари Пейдж създаде с помощта на Java и Python. Роботите на Google разглеждат и проучват интернет, опитвайки се да предоставят на потребителите си най-добрите отговори на техните въпроси. Уеб скрейпингът предлага безкрайно множество приложения и дори ако в сферата на Data Science проявявате интерес към нещо различно, определено ще ви трябват определени умения в скрейпинга, за да се сдобиете с данни за анализ.
Някои от прилаганите тук техники намерих в забележителната за уеб скрейпинга, която наскоро си набавих. В нея можете да намерите много прости примери и идеи за практически приложения на наученото. Освен това, там има много интересна глава за заобикалянето на проверките reCaptcha. За мен това беше новина, тъй като не знаех, че съществуват специализирани инструменти и дори цели услуги за решаване на подобни задачи.
Обичате ли да пътувате?!
На простия и доста безобиден въпрос, поставен в заглавието на този раздел, често може да се чуе положителен отговор, придружен с няколко истории от пътувания на този, на когото е зададен. Повечето от нас ще се съгласят, че пътуванията са прекрасен начин за потапяне в нови културни среди и разширяване на личния мироглед. Въпреки това, ако зададете на някого въпрос дали му харесва да търси самолетни билети, уверен съм, че отговорът му вече няма да бъде толкова позитивен. Всъщност, тук на помощ идва Python.
Първата задача, която трябва да решим в процеса на създаване на система за търсене на информация за самолетни билети, е изборът на подходяща платформа, от която ще извличаме информация. Решението на тази задача не беше лесно, но в крайна сметка избрах услугата Kayak. Пробвах услуги като Momondo, Skyscanner, Expedia и още няколко, но механизмите за защита от роботи на тези ресурси се оказаха непроницаеми. След няколко опита, в които, опитвайки се да убедя системите, че съм човек, трябваше да се справям със светофари, пешеходни пътеки и велосипеди, реших, че Kayak е най-подходящо за мен, дори и тук, ако за кратко време отворя твърде много страници, започват проверки. Успях да настроя бота да изпраща заявки към сайта с интервали от 4 до 6 часа и всичко работеше нормално. Периодично съществуват трудности и при работа с Kayak, но ако ви започнат да ви дразнят с проверки, трябва или да ги разрешите ръчно и след това да стартирате бота, или да изчакате няколко часа, след което проверките трябва да спрат. Ако е нужно, спокойно можете да адаптирате кода за друга платформа и ако го направите — можете да споделите в коментарите.
Ако току-що започвате да се запознавате с уеб-скрапинга и не знаете защо някои уеб сайтове толкова настоятелно се борят с него, преди да започнете първия си проект в тази област — направете си услуга и потърсете в Google материали по темата „web scraping etiquette“. Вашите експерименти могат да завършат по-бързо, отколкото предполагате, ако се занимавате с уеб-скрапинг неразумно.
Започване
Ето общ преглед на това, което ще се случва в кода на нашия уеб-скрапер:
- Импорт на необходимите библиотеки.
- Отваряне на вкладка в Google Chrome.
- Извикване на функция, която стартира бота, като му предава градовете и датите, които ще се използват при търсене на билети.
- Тази функция получава първите резултати от търсенето, подредени по критерий на най-голяма привлекателност (best), и натиска бутона за зареждане на допълнителни резултати.
- Още една функция събира данни от цялата страница и връща кадър с данни.
- Двата предишни етапа се изпълняват с помощта на типове сортиране по цена на билетите (cheap) и по скорост на полета (fastest).
- На потребителя на скрипта се изпраща имейл, съдържащ кратка справка за цените на билетите (най-евтините билети и средната цена), а таблицата с информацията, сортирана по трите споменати показателя, се запазва във файл с разширение Excel.
- Всички по-гореописани действия се извършват в цикъл след зададен интервал от време.
Трябва да се отбележи, че всеки проект Selenium започва с уеб драйвера. Аз използвам , работя с Google Chrome, но има и други възможности. Популярни са също PhantomJS и Firefox. След свалянето на драйвера, той трябва да бъде поставен в съответната папка, след което подготовката за използването му приключва. В първите редове на скрипта ни се отваря нов раздел в Chrome.
Имайте предвид, че в разказа си не се опитвам да открия нови хоризонти за търсене на изгодни оферти за самолетни билети. Съществуват и много по-напреднали техники за търсене на подобни оферти. Искам единствено да предложа на читателите на този материал прост, но практически приложим метод за решаване на тази задача.
Ето кода, за който говорихме по-рано.
from time import sleep, strftime
from random import randint
import pandas as pd
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import smtplib
from email.mime.multipart import MIMEMultipart
# Използвайте тук вашия път до chromedriver!
chromedriver_path = 'C:/{YOUR PATH HERE}/chromedriver_win32/chromedriver.exe'
driver = webdriver.Chrome(executable_path=chromedriver_path) # С тази команда се отваря прозорец Chrome
sleep(2) В началото на кода можем да видим командите за импорт на пакетите, които се използват във целия проект. Така randint се използва, за да позволи на бота да "заспа" за случайно число секунди преди да започне нова операция по търсене. Обикновено без това не може да се мине нито един бот. Ако стартирате гореописания код, ще се отвори прозорец Chrome, който ботът ще използва за работа с уебсайтовете.
Ще проведем малък експеримент и ще отворим сайта kayak.com в отделен прозорец. Ще изберем града, от който ще пътуваме, и града, в който искаме да отидем, както и датите на полетите. При избора на дати проверете да бъде използван диапазон от +-3 дни. Написах код, като съм взел предвид какви отговори дава сайтът на подобни заявки. Ако, например, трябва да търсите билети само на зададените дати, е много вероятно да трябва да доработите кода на бота. Докато говоря за кода, давам съответните обяснения, но ако почувствате, че се заплитате — дайте ми знак.
Сега натискаме бутона за стартиране на търсенето и гледаме линка в адресната лента. Той трябва да прилича на линка, който аз използвам в примера по-долу, където се обявява променливата kayak, която съхранява URL и използва метода get на уеб драйвера. След натискане на бутона за търсене на страницата трябва да се появят резултати.
![]()
Когато използвах командата get повече от две-три пъти в рамките на няколко минути, ми предлагаше да премина проверката с reCaptcha. Тази проверка може да се премине ръчно и да продължите експериментите, докато системата реши да направи нова проверка. Когато тествах скрипта, създаваше се усещането, че първия заявка за търсене винаги минава без проблеми, така че ако искате да експериментирате с кода, ще трябва периодично ръчно да преминавате проверката и да оставяте кода да се изпълнява, използвайки дълги интервали между сесиите на търсене. Да не забравяме, че на човека едва ли ще му трябват данни за цените на билетите, получени на 10-минутни интервали между операциите за търсене.
Работа с страницата с помощта на XPath
И така, отворихме прозорец и заредихме сайта. За да получим данни за цените и друга информация, трябва да използваме технологията XPath или CSS селектори. Аз реших да се спра на XPath и не почувствах нужда от използване на CSS селектори, но е напълно възможно да работите и по този начин. Придвижването по страницата с помощта на XPath може да се окаже трудно, и дори да използвате описаните от мен методи, В статия, в която се прилагаше копирането на съответните идентификатори от кода на страницата, разбрах, че всъщност това не е оптимален начин за достъп до нужните елементи. Между другото, в книгата можете да намерите отличен опис на основите на работата със страниците, използвайки XPath и CSS селектори. Ето как изглежда съответният метод на уеб драйвера.
![]()
Така че, да продължим работа по бота. Ще използваме възможностите на програмата, за да селектираме най-евтините билети. На следващото изображение кодът на XPath е обозначен в червено. За да прегледате кода, трябва да кликнете с десния бутон на мишката върху интересуващия ви елемент от страницата и в появилото се меню да изберете команда Преглед на кода (Inspect). Тази команда може да бъде извикана за различни елементи на страницата, чийто код ще бъде показан и маркиран в прозореца за преглед на кода.

Преглед на кода на страницата
За да намерите потвърждение на моите разсъждения относно недостатъците на копирането на селектори от кода, обърнете внимание на следните особености.
Ето какво се получава при копирането на кода:
//*[@id="wtKI-price_aTab"]/div[1]/div/div/div[1]/div/span/spanЗа да копирате нещо подобно, трябва да кликнете с десния бутон на мишката върху интересуващата ви част от кода и да изберете в появилото се меню командата Copy > Copy XPath.
Ето какво използвах за определяне на бутона Cheapest:
cheap_results = '\/\/a[@data-code = "price"]' 
Командата Copy > Copy XPath
Съществуват, очевидно, случаи, в които вторият вариант изглежда много по-прост. При него се извършва търсене на елемент a, който има атрибут data-code, равен на price. При използването на първия вариант се извършва търсене на елемент, чийто идентификатор е id равен на wtKI-price_aTab, като XPath пътят до елемента изглежда така /div[1]/div/div/div[1]/div/span/span. Подобен XPath запитване към страницата ще свърши работата си, но — само веднъж. В момента мога да кажа, че id ще се промени при следващото обновление на страницата. Последователността от символи wtKI се променя динамично при всяко зареждане на страницата, в резултат на което кодът, в който тя е използвана, след следващото презареждане на страницата ще бъде безполезен. Затова отделете малко време, за да разберете XPath. Тези знания ще ви бъдат много полезни.
Въпреки това трябва да се отбележи, че копирането на XPath селектори може да бъде полезно при работа с достатъчно прости сайтове, и ако това ви устройва, няма нищо лошо в това.
Сега да помислим как да действаме, ако е необходимо да получим всички резултати от търсенето в няколко реда, в списък. Много е просто. Всеки резултат се намира в обект с клас resultWrapper. Зареждането на всички резултати може да бъде извършено в цикъл, подобен на този, който ще бъде показан по-долу.
Трябва да се отбележи, че ако разбирате казаното дотук, трябва без проблем да разберете по-голямата част от кода, който ще разгледаме. По време на работата на този код, за да стигнем до това, което ни трябва (всъщност — това е елементът, в който резултатът е обвит), се обръщаме с помощта на определен механизъм за указване на пътя (XPath). Прави се това, за да получим текста на елемента и да го поставим в обект, от който можем да четем данни (първоначално се използва flight_containers, след това — flights_list).

Извеждат се първите три реда и можем ясно да видим всичко, което ни трябва. Въпреки това имаме и по-интересни начини за получаване на информация. Трябва да вземем данните от всеки елемент отделно.
Да започваме!
Най-лесно е да напиша функция за зареждане на допълнителни резултати, затова нека започнем от нея. Искам да максимизирам броя на полетите, информация за които програмата получава, и в същото време да не предизвикам подозрения у услугата, водещи до проверка, затова кликвам веднъж на бутона „Зареди повече резултати“ всеки път, когато страницата се показва. В този код трябва да се обърне внимание на блока try, който добавих, защото понякога бутона не зарежда нормално. Ако и вие се сблъскате с това — коментирайте извикванията на тази функция в кода на функцията start_kayak, която ще разгледаме по-долу.
# Загрузка большего количества результатов для того, чтобы максимизировать объём собираемых данных
def load_more():
try:
more_results = '//a[@class = "moreButton"]'
driver.find_element_by_xpath(more_results).click()
# Вывод этих заметок в ходе работы программы помогает мне быстро выяснить то, чем она занята
print('sleeping.....')
sleep(randint(45,60))
except:
passСега, след дълго разглеждане на тази функция (понякога се увличам), сме готови да обявим функция, която ще се занимава със скрапинг на страницата.
Вече съм събрал по-голямата част от необходимото в следната функция, наречена page_scrape. Понякога връщаните данни за етапите на пътя се оказват обединени, за разделянето им използвам прост метод. Например, когато за първи път използвам променливите section_a_list и section_b_list. Нашата функция връща фрейм на данни flights_df, което ни позволява да разделим резултатите, получени при използването на различни методи за сортиране на данни, а по-късно — да ги обединим.
def page_scrape():
"""This function takes care of the scraping part"""
xp_sections = '//*[@class="section duration"]'
sections = driver.find_elements_by_xpath(xp_sections)
sections_list = [value.text for value in sections]
section_a_list = sections_list[::2] # так мы разделяем информацию о двух полётах
section_b_list = sections_list[1::2]
# Если вы наткнулись на reCaptcha, вам может понадобиться что-то предпринять.
# О том, что что-то пошло не так, вы узнаете исходя из того, что вышеприведённые списки пусты
# это выражение if позволяет завершить работу программы или сделать ещё что-нибудь
# тут можно приостановить работу, что позволит вам пройти проверку и продолжить скрапинг
# я использую тут SystemExit так как хочу протестировать всё с самого начала
if section_a_list == []:
raise SystemExit
# Я буду использовать букву A для уходящих рейсов и B для прибывающих
a_duration = []
a_section_names = []
for n in section_a_list:
# Получаем время
a_section_names.append(''.join(n.split()[2:5]))
a_duration.append(''.join(n.split()[0:2]))
b_duration = []
b_section_names = []
for n in section_b_list:
# Получаем время
b_section_names.append(''.join(n.split()[2:5]))
b_duration.append(''.join(n.split()[0:2]))
xp_dates = '//div[@class="section date"]'
dates = driver.find_elements_by_xpath(xp_dates)
dates_list = [value.text for value in dates]
a_date_list = dates_list[::2]
b_date_list = dates_list[1::2]
# Получаем день недели
a_day = [value.split()[0] for value in a_date_list]
a_weekday = [value.split()[1] for value in a_date_list]
b_day = [value.split()[0] for value in b_date_list]
b_weekday = [value.split()[1] for value in b_date_list]
# Получаем цены
xp_prices = '//a[@class="booking-link"]/span[@class="price option-text"]'
prices = driver.find_elements_by_xpath(xp_prices)
prices_list = [price.text.replace('$','') for price in prices if price.text != '']
prices_list = list(map(int, prices_list))
# stops - это большой список, в котором первый фрагмент пути находится по чётному индексу, а второй - по нечётному
xp_stops = '//div[@class="section stops"]/div[1]'
stops = driver.find_elements_by_xpath(xp_stops)
stops_list = [stop.text[0].replace('n','0') for stop in stops]
a_stop_list = stops_list[::2]
b_stop_list = stops_list[1::2]
xp_stops_cities = '//div[@class="section stops"]/div[2]'
stops_cities = driver.find_elements_by_xpath(xp_stops_cities)
stops_cities_list = [stop.text for stop in stops_cities]
a_stop_name_list = stops_cities_list[::2]
b_stop_name_list = stops_cities_list[1::2]
# сведения о компании-перевозчике, время отправления и прибытия для обоих рейсов
xp_schedule = '//div[@class="section times"]'
schedules = driver.find_elements_by_xpath(xp_schedule)
hours_list = []
carrier_list = []
for schedule in schedules:
hours_list.append(schedule.text.split('n')[0])
carrier_list.append(schedule.text.split('n')[1])
# разделяем сведения о времени и о перевозчиках между рейсами a и b
a_hours = hours_list[::2]
a_carrier = carrier_list[1::2]
b_hours = hours_list[::2]
b_carrier = carrier_list[1::2]
cols = (['Out Day', 'Out Time', 'Out Weekday', 'Out Airline', 'Out Cities', 'Out Duration', 'Out Stops', 'Out Stop Cities',
'Return Day', 'Return Time', 'Return Weekday', 'Return Airline', 'Return Cities', 'Return Duration', 'Return Stops', 'Return Stop Cities',
'Price'])
flights_df = pd.DataFrame({'Out Day': a_day,
'Out Weekday': a_weekday,
'Out Duration': a_duration,
'Out Cities': a_section_names,
'Return Day': b_day,
'Return Weekday': b_weekday,
'Return Duration': b_duration,
'Return Cities': b_section_names,
'Out Stops': a_stop_list,
'Out Stop Cities': a_stop_name_list,
'Return Stops': b_stop_list,
'Return Stop Cities': b_stop_name_list,
'Out Time': a_hours,
'Out Airline': a_carrier,
'Return Time': b_hours,
'Return Airline': b_carrier,
'Price': prices_list})[cols]
flights_df['timestamp'] = strftime("%Y%m%d-%H%M") # время сбора данных
return flights_df Опитах се да нареча променливите по такъв начин, че кодът да бъде разбираем. Помнете, че променливите, които започват с a се отнасят до първия етап на пътя, а b — до втория. Преминаваме към следващата функция.
Помощни механизми
Сега имаме функция, която позволява зареждането на допълнителни резултати от търсене, и функция за обработка на тези резултати. В тази статия можехме да приключим, тъй като тези две функции осигуряват всичко необходимо за скрапване на страници, които могат да се отварят самостоятелно. Но все още не сме разгледали някои помощни механизми, за които споменахме по-рано. Например, това е кодът за изпращане на електронни писма и още някои неща. Всичко това може да се намери във функцията start_kayak, която сега ще разгледаме.
За работата на тази функция са необходими данни за градовете и датите. Тя, използвайки тези данни, формира връзка в променливата kayak, която се използва за преминаване на страницата, където ще се намират резултатите от търсенето, подредени по най-доброто съответствие с запитването. След първата сесия на скрапването ще работим с цените, намиращи се в таблицата в горната част на страницата. А именно, ще намерим минималната цена на билета и средната цена. Всичко това, заедно с прогнозата, предоставена от сайта, ще бъде изпратено по електронна поща. На страницата съответната таблица трябва да бъде в ляво горен ъгъл. Работата с тази таблица, между другото, може да предизвика грешка при търсене с използване на точни дати, тъй като в такъв случай таблицата на страницата не се извежда.
def start_kayak(city_from, city_to, date_start, date_end):
"""Кодовете на градовете - това са IATA кодовете!
Формат на датата - YYYY-MM-DD"""
kayak = ('https://www.kayak.com/flights/' + city_from + '-' + city_to +
'/' + date_start + '-flexible/' + date_end + '-flexible?sort=bestflight_a')
driver.get(kayak)
sleep(randint(8,10))
# понякога се появява изскачащ прозорец, за да се провери това и да се затвори, може да се използва блок try
try:
xp_popup_close = '//' + 'button[contains(@id,"dialog-close") and contains(@class,"Button-No-Standard-Style close ")]'
driver.find_elements_by_xpath(xp_popup_close)[5].click()
except Exception as e:
pass
sleep(randint(60,95))
print('loading more.....')
# load_more()
print('starting first scrape.....')
df_flights_best = page_scrape()
df_flights_best['sort'] = 'best'
sleep(randint(60,80))
# Вземаме най-ниската цена от таблицата, разположена в горната част на страницата
matrix = driver.find_elements_by_xpath('/*[contains(@id,"FlexMatrixCell")]')
matrix_prices = [price.text.replace('$','') for price in matrix]
matrix_prices = list(map(int, matrix_prices))
matrix_min = min(matrix_prices)
matrix_avg = sum(matrix_prices)/len(matrix_prices)
print('switching to cheapest results.....')
cheap_results = '//' + 'a[@data-code = "price"]'
driver.find_element_by_xpath(cheap_results).click()
sleep(randint(60,90))
print('loading more.....')
# load_more()
print('starting second scrape.....')
df_flights_cheap = page_scrape()
df_flights_cheap['sort'] = 'cheap'
sleep(randint(60,80))
print('switching to quickest results.....')
quick_results = '//' + 'a[@data-code = "duration"]'
driver.find_element_by_xpath(quick_results).click()
sleep(randint(60,90))
print('loading more.....')
# load_more()
print('starting third scrape.....')
df_flights_fast = page_scrape()
df_flights_fast['sort'] = 'fast'
sleep(randint(60,80))
# Запазване на новия фрейм в Excel файл, чийто име отразява градовете и датите
final_df = df_flights_cheap.append(df_flights_best).append(df_flights_fast)
final_df.to_excel('search_backups//{}_flights_{}-{}_from_{}_to_{}.xlsx'.format(strftime("%Y%m%d-%H%M"),
city_from, city_to,
date_start, date_end), index=False)
print('saved df.....')
# Може да се следи как прогнозата, издавана от сайта, съответства с реалността
xp_loading = '//' + 'div[contains(@id,"advice")]'
loading = driver.find_element_by_xpath(xp_loading).text
xp_prediction = '//' + 'span[@class="info-text"]'
prediction = driver.find_element_by_xpath(xp_prediction).text
print(loading+'n'+prediction)
# понякога в променливата loading има тази строка, която по-късно създава проблеми при изпращането на имейл
# ако това се случи - я заменяме с "Не съм сигурен"
weird = '¯_(ツ)_/¯'
if loading == weird:
loading = 'Не съм сигурен'
username = 'YOUREMAIL@hotmail.com'
password = 'YOUR PASSWORD'
server = smtplib.SMTP('smtp.outlook.com', 587)
server.ehlo()
server.starttls()
server.login(username, password)
msg = ('Subject: Flight Scrapernn
Cheapest Flight: {}nAverage Price: {}nnRecommendation: {}nnEnd of message'.format(matrix_min, matrix_avg, (loading+'n'+prediction)))
message = MIMEMultipart()
message['From'] = 'YOUREMAIL@hotmail.com'
message['to'] = 'YOUROTHEREMAIL@domain.com'
server.sendmail('YOUREMAIL@hotmail.com', 'YOUROTHEREMAIL@domain.com', msg)
print('sent email.....')Тествах този скрипт с акаунт в Outlook (hotmail.com). Не проверих как работи с акаунт в Gmail, който е доста популярен, но има много възможности. Ако използвате акаунт в Hotmail, просто въведете вашите данни в кода, за да работи всичко.
Ако искате да разберете какво точно се изпълнява в отделните участъци от кода на тази функция, можете да ги копирате и да експериментирате с тях. Експериментите с кода са единственият начин да го разберете наистина.
Готова система
Сега, когато всичко, за което говорихме, е направено, можем да създадем прост цикъл, в който се извикват нашите функции. Скриптът пита потребителя за данни за градовете и датите. При тестване с постоянно рестартиране на скрипта, вряд ли ще искате всеки път да въвеждате тези данни ръчно, затова можете временно да коментирате съответните редове и да разкоментирате тези, които следват тях, в които са зададени необходимите данни за скрипта.
city_from = input('От кой град? ')
city_to = input('Къде? ')
date_start = input('Търсене около коя дата на заминаване? Моля, използвайте само формат YYYY-MM-DD ')
date_end = input('Закъде? Моля, използвайте само формат YYYY-MM-DD ')
# city_from = 'LIS'
# city_to = 'SIN'
# date_start = '2019-08-21'
# date_end = '2019-09-07'
for n in range(0,5):
start_kayak(city_from, city_to, date_start, date_end)
print('итерация {} завърши @ {}'.format(n, strftime("%Y%m%d-%H%M")))
# Чакаме 4 часа
sleep(60*60*4)
print('чакането приключи.....') Ето как изглежда тестовият старт на скрипта.

Тестов старт на скрипта
Итог
Ако сте достигнали до този момент — поздравления! Сега разполагате с работещ уеб скрейпер, въпреки че вече виждам множество начини за неговото подобряване. Например, можете да го интегрирате с Twilio, за да изпраща вместо имейли текстови съобщения. Можете да използвате VPN или нещо друго, за да получите резултати от няколко сървъра едновременно. Има и периодично възникваща проблема с верификацията на потребителя на сайта, но и този проблем може да бъде решен. Във всеки случай, сега разполагате с основа, която при желание можете да разширите. Например, можете да направите така, че Excel файлът да се изпраща на потребителя като прикачен файл в имейл.
Само регистрирани потребители могат да участват в анкетата. , моля.
Използвате ли технологии за уеб скрейпинг?
Да
Не
Гласуваха 8 потребители. 1 потребител се въздържа.
Източник: habr.com
