Я хочу написать парер для некоторого вебсайта,
который имеет много JS кодов. Для этого я решил выб-
рать связку Selenium+PhantomJS+lxml. Работаю на Python
Парсер необходим достаточно быстрый, что бы обраба-
тывал не менее 1000 ссылок за 1 час. Для этой цели
я решил использовать многопроцессорность. (не ммного-
поточночность - из-за GIL!). Для разбиения на процессы
использовал concurrent.Future.ProcessPoolExecutor.
Проблема в следующем, я например, даю на вход 10
ссылок, на выходе обрабатываетя в лучшем случае 9
(может и 6). Это плохо! Есть еще некоторая зависимость
с увеличением количества потоков увеличивается количество
утеряных ссылок. Первое что я решил сделать, это
проследить где идет обрыв программы, где она перестает
выполняться. (assert в моем случае на сколько я понял
не пойдет, из за многопроцессорности). Тут я определил
что обрыв идет в функции browser.get(l) - не загружает
страницу. Я попробовал добавить time.sleep(x), потом
добавил wait видимый и не видимый. Тоже ничего не из-
менилось. Начал исследовать функцию get() c модуля
selenium, нашел что она перезагружать с того же модуля
функцию execute(), и там я залез в дебри, что мои
знания не позволяют разобраться, да и времени особо нет.
И в то же время, я попробвал запустить в один процесс.
То есть количество процессов = 1. И тоже одна ссылка
потерялась. Это навело на мысль, что возможно дело не
в selenium+phantomJS, а в ProcessPoolExecutor. Я заменил
этот модуль на multiproessing.Pool - и о чудо, ссылки
перестали теряться. Но вместо этого появилась другая
проблема, более 4 потоков не выполняет. Если ставишь
больше, выдает следующую ошибку:
"""
multiprocessing.pool.RemoteTraceback:
Traceback (most recent call last):
File "/usr/lib/python3.4/multiprocessing/pool.py", line 119, in worker
result = (True, func(*args, **kwds))
File "/usr/lib/python3.4/multiprocessing/pool.py", line 44, in mapstar
return list(map(*args))
File "interface.py", line 34, in hotline_to_mysql
w = Parse_hotline().browser_manipulation(link)
File "/home/water/work/parsing/class_parser/parsing_classes.py", line 352, in browser_manipulation
browser.get(l)
File "/usr/local/lib/python3.4/dist-packages/selenium/webdriver/remote/webdriver.py", line 247, in get
self.execute(Command.GET, {'url': url})
File "/usr/local/lib/python3.4/dist-packages/selenium/webdriver/remote/webdriver.py", line 233, in execute
response = self.command_executor.execute(driver_command, params)
File "/usr/local/lib/python3.4/dist-packages/selenium/webdriver/remote/remote_connection.py", line 401, in execute
return self._request(command_info[0], url, body=data)
File "/usr/local/lib/python3.4/dist-packages/selenium/webdriver/remote/remote_connection.py", line 471, in _request
resp = opener.open(request, timeout=self._timeout)
File "/usr/lib/python3.4/urllib/request.py", line 463, in open
response = self._open(req, data)
File "/usr/lib/python3.4/urllib/request.py", line 481, in _open
'_open', req)
File "/usr/lib/python3.4/urllib/request.py", line 441, in _call_chain
result = func(*args)
File "/usr/lib/python3.4/urllib/request.py", line 1210, in http_open
return self.do_open(http.client.HTTPConnection, req)
File "/usr/lib/python3.4/urllib/request.py", line 1185, in do_open
r = h.getresponse()
File "/usr/lib/python3.4/http/client.py", line 1171, in getresponse
response.begin()
File "/usr/lib/python3.4/http/client.py", line 351, in begin
version, status, reason = self._read_status()
File "/usr/lib/python3.4/http/client.py", line 321, in _read_status
raise BadStatusLine(line)
http.client.BadStatusLine: ''
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "interface.py", line 69, in <module>
main()
File "interface.py", line 63, in main
executor.map(hotline_to_mysql, link_list)
File "/usr/lib/python3.4/multiprocessing/pool.py", line 260, in map
return self._map_async(func, iterable, mapstar, chunksize).get()
File "/usr/lib/python3.4/multiprocessing/pool.py", line 599, in get
raise self._value
http.client.BadStatusLine: ''
"""
import random
import time
import lxml.html as lh
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from multiprocessing import Pool
from selenium.webdriver.common.keys import Keys
from concurrent.futures import Future, ProcessPoolExecutor, ThreadPoolExecutor
AMOUNT_PROCESS = 5
def parse(h)->list:
# h - str, html of page
lxml_ = lh.document_fromstring(h)
name = lxml_.xpath('/html/body/div[2]/div[7]/div[6]/ul/li[1]/a/@title')
prices_ = (price.text_content().strip().replace('\xa0', ' ')
for price in lxml_.xpath('//*[@id="gotoshop-price"]'))
markets_ =(market.text_content().strip() for market in
lxml_.find_class('cell shop-title'))
wares = [[name[0], market, price] for (market, price)
in zip(markets_, prices_)]
return wares
def browser_manipulation(l):
#options = []
#options.append('--load-images=false')
#options.append('--proxy={}:{}'.format(host, port))
#options.append('--proxy-type=http')
#options.append('--user-agent={}'.format(user_agent)) #тут хедеры рандомно
dcap = dict(DesiredCapabilities.PHANTOMJS)
#user agent takes from my config.py
dcap["phantomjs.page.settings.userAgent"] = (random.choice(USER_AGENT))
browser = webdriver.PhantomJS(desired_capabilities=dcap)
#print(browser)
#print('~~~~~~', l)
#browser.implicitly_wait(20)
#browser.set_page_load_timeout(80)
#time.sleep(2)
browser.get(l)
time.sleep(20)
result = parse(browser.page_source)
#print('++++++', result[0][0])
browser.quit()
return result
def main():
#open some file with links
with open(sys.argv[1], 'r') as f:
link_list = [i.replace('\n', '') for i in f]
with Pool(AMOUNT_PROCESS) as executor:
executor.map(browser_manipulation, link_list)
if __name__ == '__main__':
main()
Собственно вопросы: где может быть ошибка? из-за
селениума и фантома, ProcessPoolExecutora, или я где
то код написал не правильно?
Как можно увеличить скорость парсинга что бы 1000
ссылок, за 1 час. ?
Наконец, может есть какой то другой способ паринга
динамических страниц? (само собой на питоне)
Спасибо за ответы.