Привожу код программы для сокращение времени обработки вдвое.
Импорт
import requests #выполняет HTTP-запросы
from bs4 import BeautifulSoup #работа с HTML
import csv #работа с форматом данных CSV
from multiprocessing import Pool #предоставляет возможность параллельных процессов
Главная процедура
def main():
url = 'http://banknotes.finance.ua/'
links = []
#получение всех ссылок для парсинга с главной страницы
all_links = get_all_links(get_html(url), links)
#обеспечение многопоточности
#функции смотри help
with Pool(2) as p:
p.map(make_all, all_links)
if __name__ == '__main__':
main()
Получение URL
def get_html(url):
r = requests.get(url)
return r.text
Функции многопоточности
def make_all(url):
html = get_html(url)
data = get_page_data(html)
write_csv(data)
Получение URL главной страницы
def get_all_links(html, links):
#очистка содержимого файла - без его удаления
f=open('coin.csv', 'w')
f.close()
#работа с html-кодом, задаются параметры блоков и адрес сайта
soup = BeautifulSoup(html, 'lxml')
href = soup.find_all('div', class_= "wm_countries")
for i in href:
for link in i.find_all('a'):
links += [link['href']]
return links
Парсинг вложенных страниц
def get_page_data(html):
soup = BeautifulSoup(html, 'lxml')
try:
name = soup.find('div', 'pagehdr').find('h1').text
except:
name = ''
try:
massiv_price = [pn.find('b').text for pn in soup.find('div', class_ = 'wm_exchange').find_all('a', class_ = 'button', target = False)]+[pr.text for pr in soup.find('div', class_ = 'wm_exchange').find_all('td', class_ = 'amount')]
if len(massiv_price)==6: massiv_price=massiv_price[0]+massiv_price[3]+massiv_price[1]+massiv_price[4]+massiv_price[2]+massiv_price[5]
elif len(massiv_price)==4:
massiv_price=massiv_price[0]+massiv_price[2]+massiv_price[1]+massiv_price[3]
except:
massiv_price = ''
data = {'name': name, 'price': massiv_price}
return data
Запись файла
def write_csv(data):
with open('coin.csv', 'a') as f:
writer = csv.writer(f)
writer.writerow( (data['name'], data['price']) )
Предложенный код может быть широко использован при парсинге (и не только) с учетом особенностей сайтов.
Комментарии (16)
LingvoLena
05.03.2017 15:10-2Статья интересна, спасибо. Прошу рассмотреть парсинг сайтов с использованием lxml, urlib3 и pyparcing.
andreymal
05.03.2017 15:11+2А потом банят на неделю за слишком большую частоту запросов.
SomeOneWhoCares
05.03.2017 15:20-3На данном сайте нет защиты по IP. Но если вас это беспокоит IP-сервера можете найти в моей статье здесь.
MeGaPk
05.03.2017 16:00-4для каждого пула можно заюзать прокси + паузы в потоках, что бы не попасть под бан. Если сайт еще работает на ipv6, то покупаешь 100шт за 50 рублей и радуешься.
Skycker
05.03.2017 16:40+7Для многопоточного парсинга я бы рекомендовал Scrapy. У нее внутри
twisted
, код будет менее многословным, чем кастомное решение наbs4
и шататных питоновских возможностях работы с потоками/процессами. К тому же работа с разметкой там гораздо лаконичнее. Как-то на работе появилась задача напистать скрипт для периодического парсинга примерно 20 ресурсов на предмет упоминаний о компании клиента. Соscrapy
получилось уложиться в несколько часов.
И позвольте на минуту включить зануду и немного покритиковать оформление кода. У вас импорты не по PEP8 оформлены. В кучу смешаны вендорные пакеты и встроеные. В функции
get_all_links
зачем-то идут строки очистки файла, но, судя по названию, её задача — вытащить ссылки с главной страницы. Принцип одной отвественности говорит, что котлеты с рыбой смешивать не нужно. Да и все ссылки на сайты, имена файлов и подобное хорошо бы вынести вверх скрипта в константы. Если захотите сохранять результаты не вcoin.csv
, а вfoobar.csv
, то придется править код в двух местах. В небольшом скрипте такое, конечно, не критично, но в реальных прикладных проектах может сэкономить время и нервы коллег, поддерживающих ваш кодVovanZ
05.03.2017 17:12Scrapy однопоточный.
VovanZ
05.03.2017 21:40А за что минус? Почитайте, что ли, как
twisted
работает и что такое асинхронность. Scrapy работает в одном процессе, в одном потоке.
Ну, при желании можно запускать много отдельных процессов с помощью Scrapyd или распределённый краулинг в Scrapy Cluster, но сам Scrapy — однопоточный.
VovanZ
05.03.2017 17:13+4- Зачем вам многопоточность? Я не верю, что вы упираетесь в CPU.
- Почему вы пишете про многопоточность, но в коде используете
multiprocessing
? - Зачем писать это всё самому, когда есть Scrapy (как уже заметил комментатор выше)?
alekseev_ap
05.03.2017 22:45-4Не про питон, но по поводу парсинга сайтов: Make Collection. Из плюсов: можно качать на выбор картинки, видео, текст, звук. И есть возможность генерировать имена файлов используя окружение. Ну и до кучи — экспорт в SQLite.
MrGobus
06.03.2017 15:39+1Везет вам на питоне, я вот на node.js недавно парсер писал, так там обратная история, пришлось заморачиваться чтобы ограничить число потоков так как сервера не успевали отдать всю информацию и умирали от таймаута из за чего данные получались битыми =(
TOBBOT
06.03.2017 16:23+1Увы, плодить потоки проще и дешевле процессов. А с потоками у Python «проблема». В итоге сделать так, чтобы работало быстро можно, но не просто и не так лаконично, как в примерах выше. А пот Windows так и вообще фантастика. Хотя, я мог пропустить некий переломный момент произошедший с момента выхода Python 3.4.
andjel
07.03.2017 15:03+1Не делайте так никогда
massiv_price = [pn.find('b').text for pn in soup.find('div', class_ = 'wm_exchange').find_all('a', class_ = 'button', target = False)]+[pr.text for pr in soup.find('div', class_ = 'wm_exchange').find_all('td', class_ = 'amount')]
А еще лучше прогоните код через Flake
Scorobey
Просто и подробно о сложном — это о статье