Многопоточный веб-скребок с использованием urlretrieve на сайте с поддержкой cookie
Я пытаюсь написать свой первый Python script, и с большим количеством Googling, я думаю, что я только что закончил. Тем не менее, мне понадобится помощь, чтобы добраться до финиша.
Мне нужно написать script, который записывается на сайт с поддержкой cookie, очищает связку ссылок, а затем запускает несколько процессов для загрузки файлов. У меня есть программа, работающая в однопоточной, поэтому я знаю, что код работает. Но когда я попытался создать пул загрузчиков, я столкнулся с стеной.
#manager.py
import Fetch # the module name where worker lives
from multiprocessing import pool
def FetchReports(links,Username,Password,VendorID):
pool = multiprocessing.Pool(processes=4, initializer=Fetch._ProcessStart, initargs=(SiteBase,DataPath,Username,Password,VendorID,))
pool.map(Fetch.DownloadJob,links)
pool.close()
pool.join()
#worker.py
import mechanize
import atexit
def _ProcessStart(_SiteBase,_DataPath,User,Password,VendorID):
Login(User,Password)
global SiteBase
SiteBase = _SiteBase
global DataPath
DataPath = _DataPath
atexit.register(Logout)
def DownloadJob(link):
mechanize.urlretrieve(mechanize.urljoin(SiteBase, link),filename=DataPath+'\\'+filename,data=data)
return True
В этой версии код не работает, потому что куки файлы не были переданы работнику за использование urlretrieve. Нет проблем, я смог использовать класс mechanize.cookiejar для сохранения файлов cookie в менеджере и передать их работнику.
#worker.py
import mechanize
import atexit
from multiprocessing import current_process
def _ProcessStart(_SiteBase,_DataPath,User,Password,VendorID):
global cookies
cookies = mechanize.LWPCookieJar()
opener = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies))
Login(User,Password,opener) # note I pass the opener to Login so it can catch the cookies.
global SiteBase
SiteBase = _SiteBase
global DataPath
DataPath = _DataPath
cookies.save(DataPath+'\\'+current_process().name+'cookies.txt',True,True)
atexit.register(Logout)
def DownloadJob(link):
cj = mechanize.LWPCookieJar()
cj.revert(filename=DataPath+'\\'+current_process().name+'cookies.txt', ignore_discard=True, ignore_expires=True)
opener = mechanize.build_opener(mechanize.HTTPCookieProcessor(cj))
file = open(DataPath+'\\'+filename, "wb")
file.write(opener.open(mechanize.urljoin(SiteBase, link)).read())
file.close
Но это не так, потому что openener (я думаю) хочет переместить двоичный файл обратно в менеджер для обработки, и я получаю сообщение об ошибке "неспособность рассортировать объект", ссылаясь на веб-страницу, которую он пытается прочитать в файле.
Очевидное решение состоит в том, чтобы прочитать файлы cookie из кука файла cookie и вручную добавить их в заголовок при выполнении запроса urlretrieve, но я стараюсь избегать этого, и именно поэтому я ловим предложения.
Ответы
Ответ 1
После работы в течение большей части дня выясняется, что Mechanize не проблема, это больше похоже на ошибку кодирования. После обширной настройки и проклятия я получил код для правильной работы.
Для будущих Googlers, подобных мне, я предоставляю обновленный код ниже:
#manager.py [unchanged from original]
def FetchReports(links,Username,Password,VendorID):
import Fetch
import multiprocessing
pool = multiprocessing.Pool(processes=4, initializer=Fetch._ProcessStart, initargs=(SiteBase,DataPath,Username,Password,VendorID,))
pool.map(Fetch.DownloadJob,_SplitLinksArray(links))
pool.close()
pool.join()
#worker.py
import mechanize
from multiprocessing import current_process
def _ProcessStart(_SiteBase,_DataPath,User,Password,VendorID):
global cookies
cookies = mechanize.LWPCookieJar()
opener = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies))
Login(User,Password,opener)
global SiteBase
SiteBase = _SiteBase
global DataPath
DataPath = _DataPath
cookies.save(DataPath+'\\'+current_process().name+'cookies.txt',True,True)
def DownloadJob(link):
cj = mechanize.LWPCookieJar()
cj.revert(filename=DataPath+'\\'+current_process().name+'cookies.txt',True,True)
opener = mechanize.build_opener(mechanize.HTTPCookieProcessor(cj))
mechanize.urlretrieve(url=mechanize.urljoin(SiteBase, link),filename=DataPath+'\\'+filename,data=data)
Поскольку я просто загружаю ссылки из списка, не-поточный характер механизации не кажется проблемой [полное раскрытие: я выполнил этот процесс ровно три раза, поэтому проблема может появиться при дальнейшем тестировании], Многопроцессорный модуль и его рабочий пул делают весь тяжелый подъем. Сохранение файлов cookie в файлах было важно для меня, потому что веб-сервер, который я загружаю, должен предоставить каждому потоку собственный идентификатор сеанса, но другим пользователям, реализующим этот код, может не понадобиться его использовать. Я заметил, что кажется, что "забудьте" переменные между вызовом init и вызовом run, поэтому cookiejar не может выполнить скачок.
Ответ 2
Создание многопоточного веб-скребка - правильный путь. Я уверен, что вы справитесь с этим, но почему бы не использовать то, что уже сделано?
Я действительно предлагаю вам проверить Scrapy http://scrapy.org/
Это очень гибкая инфраструктура скрепок с открытым исходным кодом, которая будет обрабатывать большинство материалов, которые вам понадобятся здесь. С помощью Scrapy запуск параллельных пауков - проблема конфигурации, а не проблема программирования (http://doc.scrapy.org/topics/settings.html#concurrent-requests-per-spider). Вы также получите поддержку файлов cookie, прокси, HTTP-аутентификации и т.д.
Для меня потребовалось около 4 часов, чтобы переписать мой скребок в Scrapy. Поэтому, пожалуйста, спросите себя: действительно ли вы хотите решить проблему с потоками самостоятельно или вместо этого забраться на плечи других и сосредоточиться на проблемах веб-соскабливания, а не на потоке?
PS. Вы используете механизацию сейчас? Обратите внимание на это из механизации FAQ http://wwwsearch.sourceforge.net/mechanize/faq.html:
"Это потокобезопасность?
Нет. Насколько я знаю, вы можете использовать механизацию в потоковом коде, но она не обеспечивает синхронизацию: вы должны предоставить это сами. "
Если вы действительно хотите использовать механизацию, начните чтение документации о том, как обеспечить синхронизацию. (например, http://effbot.org/zone/thread-synchronization.htm, http://effbot.org/pyfaq/what-kinds-of-global-value-mutation-are-thread-safe.htm)
Ответ 3
Чтобы включить сеанс cookie в первом примере кода, добавьте следующий код в функцию DownloadJob:
cj = mechanize.LWPCookieJar()
opener = mechanize.build_opener(mechanize.HTTPCookieProcessor(cj))
mechanize.install_opener(opener)
И тогда вы можете получить URL-адрес, как вы:
mechanize.urlretrieve(mechanize.urljoin(SiteBase, link),filename=DataPath+'\\'+filename,data=data)