Многопоточный веб-скребок с использованием urlretrieve на сайте с поддержкой файлов cookie - PullRequest
2 голосов
/ 24 мая 2011

Я пытаюсь написать свой первый скрипт на Python, и с большим количеством Google, я думаю, я почти закончил.Тем не менее, мне понадобится некоторая помощь, чтобы добраться до финиша.

Мне нужно написать скрипт, который будет входить на сайт с поддержкой 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

В этой ревизии код завершается ошибкой, поскольку файлы cookie не были переданы работнику для использования в качестве urlretrieve.Нет проблем, я смог использовать класс механизатора .cookiejar, чтобы сохранить куки в менеджере и передать их работнику.

#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

Но, ЭТО терпит неудачу, потому что новичок (я думаю) хочет переместитьБинарный файл возвращается к менеджеру для обработки, и я получаю сообщение об ошибке «Невозможно засолить объект», ссылаясь на веб-страницу, которую он пытается прочитать в файл.

Очевидное решение - прочитать файлы cookie избанку печенья и вручную добавьте их в заголовок при выполнении запроса urlretrieve, но я пытаюсь избежать этого, и именно поэтому я ищу предложения.

Ответы [ 3 ]

5 голосов
/ 25 мая 2011

Правильно создать многопоточный веб-скребок очень сложно.Я уверен, что вы могли бы справиться с этим, но почему бы не использовать что-то, что уже было сделано?

Я действительно действительно предлагаю вам проверить Scrapy http://scrapy.org/

Это очень гибкое открытиеИсходный веб-скребковый фреймворк, который будет обрабатывать большинство вещей, которые вам также понадобятся здесь.В Scrapy запуск параллельных пауков - это проблема конфигурации, а не проблема программирования (http://doc.scrapy.org/topics/settings.html#concurrent-requests-per-spider).. Вы также получите поддержку для файлов cookie, прокси-серверов, HTTP-аутентификации и многого другого.

Для меня это заняло около4 часа, чтобы переписать мой скребок в Scrapy. Поэтому, пожалуйста, спросите себя: действительно ли вы хотите решить проблему с потоками самостоятельно или вместо этого лезть на плечи других и сосредоточиться на проблемах с веб-очисткой, а не с потоками?

PS. Используете ли вы сейчас Mechanize? Обратите внимание на это в 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)

4 голосов
/ 25 мая 2011

После работы в течение большей части дня выясняется, что проблема не в Mechanize, это больше похоже на ошибку кодирования. После тщательной настройки и проклятия, я получил код для правильной работы.

Для будущих пользователей Google, таких как я, я предоставляю обновленный код ниже:

#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 может не выполнить переход.

0 голосов
/ 01 ноября 2013

Чтобы включить сеанс 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)
...