Импортируем необходимые модули и библиотеки:
sleep
: для выставления задержки по времени;bs4
: для обработки кода HTML с помощью BeautifulSoup
.import time
from bs4 import BeautifulSoup
Теперь импортируем необходимые модули selenium
. Они все те же, модуль webdriver
для соединения Python с браузером через драйвер и модуль для поиска By
:
from selenium import webdriver as wd
from selenium.webdriver.common.by import By
Открываем новое окно браузера:
br = wd.Chrome(executable_path = "/Users/allat/Documents/chromedriver")
/opt/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:1: DeprecationWarning: executable_path has been deprecated, please pass in a Service object """Entry point for launching an IPython kernel.
Отправим запрос к странице с профилем Норвегии за 2000 год на сайте OEC, где, в числе прочего, находятся данные по экспорту стран. Технически, после country
в ссылку ниже можно подставить аббревиатру для любой страны, а после yearSelector1
– любой год (вспоминаем про форматирование строк и f-строки):
url = "https://oec.world/en/profile/country/nor?depthSelector1=HS4Depth&yearSelector1=2000"
print(url)
https://oec.world/en/profile/country/nor?depthSelector1=HS4Depth&yearSelector1=2000
# подождем 5 секунд, сайт тяжелый
br.get(url)
time.sleep(5)
Предположим, нам нужна информация об объеме экспорта различных товаров из мозаичного графика, который появляется только тогда, когда мы доскролливаем до раздела Historical data где-то в середине страницы.
Если открыть страницу в браузере (не с Selenium, чтобы не мешать) и выбрать Просмотреть код, откроются инструменты разработчика. При работе в режиме разработчика можно находить элементы на странице, а соответствующие им фрагменты будут выделяться в исходном коде. Так вот: до тех пор пока мы не дойдем до нужного момента, в исходном коде просто не будет блока с данными об экспорте. Страница обновляется динамически, ее наполнение изменяется в зависимости от действий пользователя. То же самое будет, если мы откроем полностью исходный код страницы в отдельной вкладке. Если попробовать найти код с текстом Historical data, ничего не получится.
Поэтому воспользуемся возможностями Selenium и организуем скроллинг. К объекту br
, в котором у нас сохранено соединение с браузером, можно применить метод .execute_script()
для исполнения кода на языке JavaScript, отвечающего за интерактив на веб-страницах. А код будет такой:
window.scrollTo(0, document.body.scrollHeight);
Функция window.scrollTo()
реализует прокрутку страницы, а в качестве аргументов этой функции мы указываем начальную и конечную точку. Начальная точка 0, а конечная – конец страницы, который вычисляется автоматически, извлекается из информации о документе.
br.execute_script("window.scrollTo(0, document.body.scrollHeight);")
Итак, страница в браузере пролисталась до конца. Для примера вернемся назад:
br.execute_script("window.scrollTo(0, 0);")
А теперь проскроллим на 2500 пикселей, чтобы дойти до нужного места с Historical Data (значение подобрано экспериментально, на глаз):
br.execute_script("window.scrollTo(0, 2500);")
time.sleep(2)
Готово! Нужный фрагмент теперь точно есть на странице. Найдем его, используя XPATH (вот тут, конечно, сложновато, это последовательность тэгов, которые нужно пройти, чтобы оказаться в нужной части):
part = br.find_element(By.XPATH,
'.//*[@id="cp-section-625"]/div/div[2]/div[1]/div[2]/div')
Исходный код всей страницы нам не нужен, заберем только фрагмент, который хранится в part
выше:
html = part.get_attribute('innerHTML')
Преобразуем текст в объект BeautifulSoup:
soup = BeautifulSoup(html)
Если внимательно изучить исходный код, можно заметить, что нужная информация об экспорте хранится в тэгах <g>
с определенным классом. Найдем все подходящие фрагменты кода HTML:
# ищем блоки с текстом через методы BeautifulSoup
blocks = soup.find_all("g", {"class" : "d3plus-textBox"})
Извлечем текст из каждого блока и посмотрим на результаты:
texts = [block.text for block in blocks]
print(texts)
['Total: $64.5B', 'Crude Petroleum', '44.9%', 'PetroleumGas', '9.39%', 'RefinedPetroleum', '8.13%', 'RawAluminium', '3.13%', 'Non-filletFresh Fish', '1.9%', 'Non-filletFrozenFish', '1.32%', 'Passenger andCargo Ships', '1.08%', 'FishFillets', '1.02%', 'Ferroalloys', '0.99%', 'Raw Nickel', '0.92%', 'Fish: dried,salted, smoked...', '0.87%', 'Newsprint', '0.75%', '', '0.65%', 'Gas...', '0.56%', 'Motor...', '0.51%', '', '0.47%', '', '0.45%', 'Mixed...', '0.44%', 'Computers', '0.38%', '', '0.33%', '', '', 'Telephones', '', '', '', '', '', '', '', 'Semi-...', '', 'Kraft...', '', 'Liquid...', 'Planes...', '', '', '', '', '', 'Raw...', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
Не очень красиво, но если нам нужна информация по общему объему экспорта и процентам, которые приходятся на экспорт самых «топовых» товаров, для этого у нас есть все данные. А со списками работать мы точно умеем.