Python для сбора данных

Алла Тамбовцева, НИУ ВШЭ

Работа с selenium: продолжение

Сегодня мы продолжим работать над задачей, поставленной ранее — выгрузка адресов всех участковых избирательных комиссий Ивановской области. Сначала загрузим все необходимые для работы библиотеки и функции:

  • selenium — для автоматизации работы в браузере;
  • BeautifulSoup — для поиска по html-коду;
  • time — для добавления задержки;
  • pandas — для сохранения результатов в датафрейм.
In [1]:
from time import sleep
from bs4 import BeautifulSoup
import pandas as pd
In [2]:
from selenium import webdriver as wb
br = wb.Chrome("/Users/allat/Downloads/chromedriver")

br.implicitly_wait(2)  # чтобы страница прогрузилась

Теперь напишем функцию get_uik_address(), которая принимает на вход два аргумента, номер участка и регион, и возвращает строку с адресом. Для этого в тело функции скопируем код с прошлого занятия.

Только давайте перестрахуемся — напишем выражение с исключением, чтобы в случае, если страница не содержит адреса или загружается некорректно, наш код не ломался. В случае, если всё хорошо (адрес есть), Python будет его сохранять («ветка» c try), в случае, если всё плохо (адреса нет ни в каком виде), Python будет записывать вместо него None (ветка с except) и двигаться дальше.

In [3]:
def get_uik_address(n_uik, reg):
    
    br.get("http://www.cikrf.ru/services/lk_address/?do=find_by_uik")
    uik_field = br.find_element_by_css_selector("#uik")
    uik_field.send_keys(n_uik)
    
    region_field = br.find_element_by_name("subject")
    region_field.send_keys(reg)
    
    button = br.find_element_by_link_text("Отправить запрос")
    button.click()
    sleep(1) # добавим задержку в 1 секунду
    
    soup = BeautifulSoup(br.page_source, 'lxml')
    texts = [a.text for a in soup.find_all('p')] 
    
    try:
        address = list(filter(lambda x: "Адрес помещения для голосования:" in x, 
           texts))[0]  
    except:
        address = None
    return n_uik, address

Теперь попробуем взять несколько номеров участков и посмотреть, что получается в цикле.

In [4]:
uiks = range(200, 216)
In [5]:
addresses = []

for u in uiks:
    addresses.append(get_uik_address(u, "Ивановская область"))
    print(u)
    sleep(1.5)
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215

Работает! Создадим список со всеми номерами избирательных участков Ивановской области:

In [6]:
ivanovo = range(1, 777) # вроде все, см здесь новый список - http://www.ivanovo.izbirkom.ru/docs/4272/

Внимание: исполнение следующей ячейки займет много времени (час-полтора часа). Если просто хотите посмотреть, как это работает (не выгружая информацию по всем участкам), уменьшите правое значение в range() в ячейке выше.

In [13]:
ivanovo_addr = []

for i in ivanovo:
    ivanovo_addr.append(get_uik_address(i, "Ивановская область"))
    #print(i)
    sleep(1.5)

Важно: периодически открывайте окно браузера, в котором Python ищет избирательные участки! Это не только приятно (смотреть, как в полях для поиска все заполняется без нашего участия), но и полезно: так можно заметить, если что-то пошло не так. История из жизни: опечаталась в букве внутри цикла, Python 777 раз открыл страницу с избирательным участком 244 и сохранил одинаковые адреса.

Создадим датафрейм из списка выше.

In [14]:
df = pd.DataFrame(ivanovo_addr)
In [16]:
df.head()
Out[16]:
0 1
0 1 Адрес помещения для голосования: 153012, Ивано...
1 2 Адрес помещения для голосования: 153012, Ивано...
2 3 Адрес помещения для голосования: 153000, Ивано...
3 4 Адрес помещения для голосования: 153012, Ивано...
4 5 Адрес помещения для голосования: 153012, Ивано...

Сохраним таблицу в Excel-файл:

In [17]:
df.to_csv('Ivanovo.xlsx')