hui_v_reduzah
Опытный user
- Регистрация
- 11 Янв 2021
- Сообщения
- 1,132
- Реакции
- 14
Итак, сразу к делу.
Что такое Selenium?
Selenium WebDriver — это инструмент для автоматизации действий веб-браузера. В большинстве случаев используется для тестирования Web-приложений, но этим не ограничивается. В частности, он может быть использован для решения рутинных задач администрирования сайта или регулярного получения данных из различных источников.
Почему мы используем Selenium, а не обычные python requests? OLX требует выполнения JavaScript кода для прогрузки страницы, чего не могут себе позволить обычные реквесты.
Что нужно для написания парсера?
Во-первых нужно установить последний python с официального сайта и любую IDE, я советую PyCharm
Во-вторых нужно установить драйвер Google Chrome для Selenium (именно его я использую), для этого установите браузер хром и также скачайте отсюда сам драйвер и перекиньте его в папку Scripts (она находится рядом с exe'шником python)
https://chromedriver.chromium.org/downloads
Суть
Сначала добавим следующие импорты:
import os
import sys
from os import path
import time
import xlsxwriter
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
from bs4 import BeautifulSoup
from urllib.parse import urlparse
По импортам уже видно, что будем использовать, надеюсь вы читали мой первый гайд, ибо про BeautifulSoup я не буду рассказывать в этот раз.
Создадим класс и функцию __init__ для инициализации глобальных переменных (рандомный уид для таблицы и ссылка на каталог товаров):
class OlxParser:
def __init__(self, uid, url):
self.uid = uid
self.url = url
И пишем главную функцию get(), а начнем именно с инициализации нашего веб драйвера:
def get(self):
all_ads = []
a = "https://" + urlparse(self.url).hostname + ""
options = webdriver.ChromeOptions()
options.add_argument('--ignore-certificate-errors')
options.add_argument('--ignore-ssl-errors')
options.add_argument('--ignore-gpu-blacklist')
options.add_argument('--use-gl')
options.add_argument('--disable-web-security')
options.add_experimental_option("excludeSwitches", ['enable-logging'])
options.add_extension(path.abspath('adblock.crx'))
driver = webdriver.Chrome(chrome_options=options)
Тут выше все аргументы браузера, чтобы оптимизировать и исключить все ошибки. Также закиньте желательно расширение adblock plus рядом со скриптом, а то OLX любит на страницу до 20 баннеров подгружать, что замедляет загрузку страницы в 2-4 раза.
Теперь давайте загрузим страницу с товарами и скроем уведомление о том, что OLX использует наши Cookies:
driver.get(self.url)
time.sleep(3)
driver.execute_script("javascript:void(0);")
#ActionChains(driver).send_keys(Keys.CONTROL, Keys.TAB).perform()
actions = ActionChains(driver)
actions.send_keys(Keys.TAB)
actions.send_keys(Keys.ENTER)
actions.perform()
И теперь пишем следующий код, обьясню я его ниже.
soup = BeautifulSoup(driver.page_source, "lxml")
for ads in soup.findAll('div', {'class': 'offer-wrapper'}):
try:
url = ads.findAll("a")[0]['href']
print(url)
if len(str(url)) > 20:
driver.get(url)
ad_soup = BeautifulSoup(driver.page_source, "lxml")
try:
button = ad_soup.findAll('button', {'data-testid': 'show-phone'})[0].text
show_phone = driver.find_element(By.XPATH, '//button[text()="' + button + '"]')
show_phone.click()
except:
print()
time.sleep(1)
phone = "000 000 000"
lies = driver.find_elements_by_tag_name('li')
for li in lies:
if str(li.text).replace(" ", "").replace("+", "").isdecimal():
phone = li.text
section = ad_soup.findAll('section')[0]
section2 = ad_soup.findAll('section')[3]
_all_ads = {}
_all_ads["title"] = ad_soup.findAll('h1', {'data-cy': 'ad_title'})[0].text
_all_ads["price"] = ad_soup.findAll('div', {'data-testid': 'ad-price-container'})[0].text
_all_ads["phone"] = phone
_all_ads["description"] = ad_soup.findAll('div', {'data-cy': 'ad_description'})[0].text
_all_ads["posted"] = ad_soup.findAll('span', {'data-cy': 'ad-posted-at'})[0].text
try:
_all_ads["viewed"] = ad_soup.findAll('span', {'data-testid': 'page-view-text'})[0].text
except:
_all_ads["viewed"] = " "
_all_ads["seller_name"] = section.findAll('h2')[0].text
try:
_all_ads["location"] = section2.findAll('div')[0].text
except:
_all_ads["location"] = "None"
_all_ads["url"] = url
all_ads.append(_all_ads)
except Exception as e:
print("[~] error: " + str(e) + ". line: " + str(sys.exc_info()[-1].tb_lineno))
driver.close()
И так, сначала скрипт инициализирует BeautifulSoup и затем последовательно в цикле обрабатывает все div'ы с классом offer-wrapper, именно это и есть объекты с товарами. Затем достает оттуда ссылку с помощью BS4 и переходить драйвером по ней. Дальше драйвер ищет кнопку для открытия номера телефона и открывает ее, если она есть. Потом также в цикле с помощью драйвера обрабатываются все <li> элементы на странице, ведь именно там хранится номер телефона. И если текст полученного элемента из цифр, то он сохраняется в переменную phone.
Думаю дальше объяснять работу BS4 мне не нужно, ведь это есть в моем первом гайде. (Идет сам процесс парсинга каждого важного значения) И закрываем драйвер, ибо он больше не нужен.
Отлично, а что теперь делать с массивом объявлений? Его нужно поместить в таблицу Excel вот таким образом:
if all_ads is not None:
table_name = 'ultimate-sorter-olx-' + str(self.uid) + '.xlsx'
workbook = xlsxwriter.Workbook(table_name)
worksheet = workbook.add_worksheet()
worksheet.write(0, 0, "id")
worksheet.write(0, 1, "title")
worksheet.write(0, 2, "price")
worksheet.write(0, 3, "phone")
worksheet.write(0, 4, "posted")
worksheet.write(0, 5, "location")
worksheet.write(0, 6, "description")
worksheet.write(0, 7, "seller_name")
#worksheet.write(0, 8, "seller_registered")
#worksheet.write(0, 9, "seller_active_items")
worksheet.write(0, 8, "url")
for i in range(len(all_ads)):
try:
j = all_ads
l = i + 1
worksheet.write(l, 0, i)
worksheet.write(l, 1, j["title"])
worksheet.write(l, 2, j["price"])
worksheet.write(l, 3, j["phone"])
worksheet.write(l, 4, j["posted"])
worksheet.write(l, 5, j["location"])
worksheet.write(l, 6, j["description"])
worksheet.write(l, 7, j["seller_name"])
#worksheet.write(l, 8, j["seller-joined"])
#worksheet.write(l, 9, j["seller_items"])
worksheet.write(l, 8, j["url"])
except:
print("xlsx row error")
workbook.close()
return os.path.abspath(table_name)
else:
return "error"
И последний штрих. В самом конце кода стоит вызвать, где id - рандомная строка, а url - каталог с товарами.
OlxParser(id, url).get()
Итоги
В консоли после запуска будет примерно такая картина:
А после выполнения скрипта, вы получите красивую таблицу:
Всем парсеров, друзья!
Что такое Selenium?
Selenium WebDriver — это инструмент для автоматизации действий веб-браузера. В большинстве случаев используется для тестирования Web-приложений, но этим не ограничивается. В частности, он может быть использован для решения рутинных задач администрирования сайта или регулярного получения данных из различных источников.
Почему мы используем Selenium, а не обычные python requests? OLX требует выполнения JavaScript кода для прогрузки страницы, чего не могут себе позволить обычные реквесты.
Что нужно для написания парсера?
Во-первых нужно установить последний python с официального сайта и любую IDE, я советую PyCharm
Во-вторых нужно установить драйвер Google Chrome для Selenium (именно его я использую), для этого установите браузер хром и также скачайте отсюда сам драйвер и перекиньте его в папку Scripts (она находится рядом с exe'шником python)
https://chromedriver.chromium.org/downloads
Суть
Сначала добавим следующие импорты:
import os
import sys
from os import path
import time
import xlsxwriter
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
from bs4 import BeautifulSoup
from urllib.parse import urlparse
По импортам уже видно, что будем использовать, надеюсь вы читали мой первый гайд, ибо про BeautifulSoup я не буду рассказывать в этот раз.
Создадим класс и функцию __init__ для инициализации глобальных переменных (рандомный уид для таблицы и ссылка на каталог товаров):
class OlxParser:
def __init__(self, uid, url):
self.uid = uid
self.url = url
И пишем главную функцию get(), а начнем именно с инициализации нашего веб драйвера:
def get(self):
all_ads = []
a = "https://" + urlparse(self.url).hostname + ""
options = webdriver.ChromeOptions()
options.add_argument('--ignore-certificate-errors')
options.add_argument('--ignore-ssl-errors')
options.add_argument('--ignore-gpu-blacklist')
options.add_argument('--use-gl')
options.add_argument('--disable-web-security')
options.add_experimental_option("excludeSwitches", ['enable-logging'])
options.add_extension(path.abspath('adblock.crx'))
driver = webdriver.Chrome(chrome_options=options)
Тут выше все аргументы браузера, чтобы оптимизировать и исключить все ошибки. Также закиньте желательно расширение adblock plus рядом со скриптом, а то OLX любит на страницу до 20 баннеров подгружать, что замедляет загрузку страницы в 2-4 раза.
Теперь давайте загрузим страницу с товарами и скроем уведомление о том, что OLX использует наши Cookies:
driver.get(self.url)
time.sleep(3)
driver.execute_script("javascript:void(0);")
#ActionChains(driver).send_keys(Keys.CONTROL, Keys.TAB).perform()
actions = ActionChains(driver)
actions.send_keys(Keys.TAB)
actions.send_keys(Keys.ENTER)
actions.perform()
И теперь пишем следующий код, обьясню я его ниже.
soup = BeautifulSoup(driver.page_source, "lxml")
for ads in soup.findAll('div', {'class': 'offer-wrapper'}):
try:
url = ads.findAll("a")[0]['href']
print(url)
if len(str(url)) > 20:
driver.get(url)
ad_soup = BeautifulSoup(driver.page_source, "lxml")
try:
button = ad_soup.findAll('button', {'data-testid': 'show-phone'})[0].text
show_phone = driver.find_element(By.XPATH, '//button[text()="' + button + '"]')
show_phone.click()
except:
print()
time.sleep(1)
phone = "000 000 000"
lies = driver.find_elements_by_tag_name('li')
for li in lies:
if str(li.text).replace(" ", "").replace("+", "").isdecimal():
phone = li.text
section = ad_soup.findAll('section')[0]
section2 = ad_soup.findAll('section')[3]
_all_ads = {}
_all_ads["title"] = ad_soup.findAll('h1', {'data-cy': 'ad_title'})[0].text
_all_ads["price"] = ad_soup.findAll('div', {'data-testid': 'ad-price-container'})[0].text
_all_ads["phone"] = phone
_all_ads["description"] = ad_soup.findAll('div', {'data-cy': 'ad_description'})[0].text
_all_ads["posted"] = ad_soup.findAll('span', {'data-cy': 'ad-posted-at'})[0].text
try:
_all_ads["viewed"] = ad_soup.findAll('span', {'data-testid': 'page-view-text'})[0].text
except:
_all_ads["viewed"] = " "
_all_ads["seller_name"] = section.findAll('h2')[0].text
try:
_all_ads["location"] = section2.findAll('div')[0].text
except:
_all_ads["location"] = "None"
_all_ads["url"] = url
all_ads.append(_all_ads)
except Exception as e:
print("[~] error: " + str(e) + ". line: " + str(sys.exc_info()[-1].tb_lineno))
driver.close()
И так, сначала скрипт инициализирует BeautifulSoup и затем последовательно в цикле обрабатывает все div'ы с классом offer-wrapper, именно это и есть объекты с товарами. Затем достает оттуда ссылку с помощью BS4 и переходить драйвером по ней. Дальше драйвер ищет кнопку для открытия номера телефона и открывает ее, если она есть. Потом также в цикле с помощью драйвера обрабатываются все <li> элементы на странице, ведь именно там хранится номер телефона. И если текст полученного элемента из цифр, то он сохраняется в переменную phone.
Думаю дальше объяснять работу BS4 мне не нужно, ведь это есть в моем первом гайде. (Идет сам процесс парсинга каждого важного значения) И закрываем драйвер, ибо он больше не нужен.
Отлично, а что теперь делать с массивом объявлений? Его нужно поместить в таблицу Excel вот таким образом:
if all_ads is not None:
table_name = 'ultimate-sorter-olx-' + str(self.uid) + '.xlsx'
workbook = xlsxwriter.Workbook(table_name)
worksheet = workbook.add_worksheet()
worksheet.write(0, 0, "id")
worksheet.write(0, 1, "title")
worksheet.write(0, 2, "price")
worksheet.write(0, 3, "phone")
worksheet.write(0, 4, "posted")
worksheet.write(0, 5, "location")
worksheet.write(0, 6, "description")
worksheet.write(0, 7, "seller_name")
#worksheet.write(0, 8, "seller_registered")
#worksheet.write(0, 9, "seller_active_items")
worksheet.write(0, 8, "url")
for i in range(len(all_ads)):
try:
j = all_ads
l = i + 1
worksheet.write(l, 0, i)
worksheet.write(l, 1, j["title"])
worksheet.write(l, 2, j["price"])
worksheet.write(l, 3, j["phone"])
worksheet.write(l, 4, j["posted"])
worksheet.write(l, 5, j["location"])
worksheet.write(l, 6, j["description"])
worksheet.write(l, 7, j["seller_name"])
#worksheet.write(l, 8, j["seller-joined"])
#worksheet.write(l, 9, j["seller_items"])
worksheet.write(l, 8, j["url"])
except:
print("xlsx row error")
workbook.close()
return os.path.abspath(table_name)
else:
return "error"
И последний штрих. В самом конце кода стоит вызвать, где id - рандомная строка, а url - каталог с товарами.
OlxParser(id, url).get()
Итоги
В консоли после запуска будет примерно такая картина:

А после выполнения скрипта, вы получите красивую таблицу:

Всем парсеров, друзья!