Python многопроцессорной очистки, дублировать результаты - PullRequest
0 голосов
/ 09 ноября 2018

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

Внедрение многопроцессорной обработки приводит к некоторым странным результатам, которые я не смог объяснить. Если я запускаю этот код, устанавливая значение пула равным 1 (следовательно, без многопоточности), я получаю конечный результат, в котором у меня есть 0,5% дублированных ссылок (что достаточно справедливо). Как только я ускоряю его, устанавливая значение 8, 12 или 24, я получаю около 25% повторяющихся ссылок в окончательных результатах.

Я подозреваю, что моя ошибка в том, что я записываю результаты в файл csv или в способ, которым я использую функцию imap() (то же самое происходит с imap_unordered, map и т. Д.), Которая приводит потоки чтобы каким-то образом получить доступ к тем же элементам на проходной. Любое предложение?

#!/usr/bin/env python
#  coding: utf8
import sys
import requests, re, time
from bs4 import BeautifulSoup
from lxml import etree
from lxml import html
import random
import unicodecsv as csv
import progressbar
import multiprocessing
from multiprocessing.pool import ThreadPool

keyword = "euro"

def openup():
    global crawl_list
    try:
        ### Generate list URLS based on the number of results for the keyword, each of these contains other links. The list is subsequently randomized
        startpage = 1
        ## Get endpage
        url0 = 'https://www.lemonde.fr/recherche/?keywords='+str(keyword)+'&page_num=1&operator=and&exclude_keywords=&qt=recherche_texte_titre&author=&period=since_1944&start_day=01&start_month=01&start_year=1944&end_day=30&end_month=10&end_year=2018&sort=asc'
        r0 = requests.get(url0)
        print "First request: "+str(r0.status_code)
        tree = html.fromstring(r0.content)
        endpage = tree.xpath("//*[@id='habillagepub']/div[5]/div/div[1]/section/div/ul/li[@class='adroite']/a/text()")
        print str(endpage[0]) + " pages found"
        ### Generate random sequence for crawling
        crawl_list = random.sample(range(1,int(endpage[0])+1), int(endpage[0]))
        return crawl_list
    except Exception as e:
        ### Catches openup error and return an empty crawl list, then breaks
        print e 
        crawl_list = []
        return crawl_list

def worker_crawl(x):
    ### Open page
    url_base = 'https://www.lemonde.fr/recherche/?keywords='+str(keyword)+'&page_num='+str(x)+'&operator=and&exclude_keywords=&qt=recherche_texte_titre&author=&period=since_1944&start_day=01&start_month=01&start_year=1944&end_day=30&end_month=10&end_year=2018&sort=asc'
    r = requests.get(url_base)
    print "Connecting to page " + str(x) +" ..."+ str(r.status_code)
    while True:
        if r.status_code == 200:
            tree = html.fromstring(r.content)
            ### Get data 
            titles = tree.xpath('//*[@id="habillagepub"]/div[5]/div/div[1]/section/article/div/div/h3/a/text()')
            links = tree.xpath('//*[@id="habillagepub"]/div[5]/div/div[1]/section/article/div/div/h3/a/@href')
            abstracts = tree.xpath('//*[@id="habillagepub"]/div[5]/div/div[1]/section/article/div/div/p/text()')
            footers = tree.xpath('//*[@id="habillagepub"]/div[5]/div/div[1]/section/article/div/div/span/text()')
            dates = []
            pagenums = []
            for f in footers:
                pagenums.append(x)
                match = re.search(r'\| .+$', f)
                if match:
                    date = match.group()
                    dates.append(date)
            pageindex = zip(titles,links,abstracts,footers,dates,pagenums) #what if there is a missing value?
            return pageindex
        else:
            pageindex = [[str(r.status_code),"","","","",str(x)]]
            return pageindex
            continue

def mp_handler():
    ### Write down:
    with open(keyword+'_results.csv', 'wb') as outcsv:
        wr = csv.DictWriter(outcsv, fieldnames=["title","link","abstract","footer","date","pagenum"])
        wr.writeheader()
        results = p.imap(worker_crawl, crawl_list)
        for result in results:
            for x in result:
                wr.writerow({
                    #"keyword": str(keyword),
                    "title": x[0],
                    "link": x[1],
                    "abstract": x[2],
                    "footer": x[3],
                    "date": x[4],
                    "pagenum": x[5],
                    })

if __name__=='__main__':
        p = ThreadPool(4)
        openup()
        mp_handler()
        p.terminate()
        p.join()

1 Ответ

0 голосов
/ 11 декабря 2018

Вы уверены, что страница отвечает правильным ответом в быстрой последовательности запросов? Я был в ситуациях, когда очищенный сайт отвечал по-разному, если запросы были быстрыми, а если запросы были распределены во времени. Во время отладки все прошло идеально, но как только запросы были быстрыми и последовательными, веб-сайт решил дать мне другой ответ. Кроме того, я хотел бы спросить, может ли повлиять тот факт, что вы пишете в не поточнобезопасной среде: чтобы минимизировать взаимодействия в конечном выводе CSV и проблемы с данными, вы можете:

...