关于Python中的线程

时间:2014-01-30 00:01:50

标签: python multithreading

我有一个由我自己制作的小型python程序,它为一些网站提供了一些价格。我正在使用beautifulsoup 4和python线程模块。

问题是我不知道如何“控制”线程。从代码中可以看出,我创建了线程类的子类(类似于consumer,producer)。在一个班级中,我从页面中获取链接,而在另一个班级中,我正在使用BS4在html中查找一些类并写入主文件。

当我启动脚本时,我通常从线程1开始。我正在抓取网站上的每个链接,获取名称和文章价格。对于每个链接,我都在制作线程。由于网站有很多链接(大约3000个),经过一段时间后,我有那么多线程正在杀死我的电脑。 Python.exe大约2 GB,我必须杀死该程序。

这是我第四天试图寻找解决方案......请.... :)

如果我说得对:setDaemon(true) - 程序在执行后杀死它们.join()等待完成线程。

我完全是编程的初学者,也知道代码有点乱。欢迎任何建议。

不要担心最后几个试块。它只是为了好玩。

谢谢!

import threading
import csv
import urllib2
import time
from bs4 import BeautifulSoup
import re
import Queue


httpLink = "WWW.SOMEWEBSITE.COM"
fn = 'J:\\PRICES\\'

queue = Queue.Queue()
soup_queue = Queue.Queue()
brava = threading.Lock()

links = []
brokenLinks = []
pageLinks = []

fileName = time.strftime("%d_%m_%Y-%H_%M")


class TakeURL(threading.Thread):
    def __init__(self, queue, soup_queue):
        threading.Thread.__init__(self)
        self.queue = queue
        self.soup_queue = soup_queue

    def run(self):
        while True:
            host = self.queue.get()
            try:
                url = urllib2.urlopen(host)
                chunk = url.read()
            except:
                print ("Broken link " + host)
                writeCSV("BrokenLinks.csv", "ab", host)
                brokenLinks.append(host)
                time.sleep(30)

            writeCSV('Links.csv','ab',host)

            if ("class=\"price\"" in chunk):
                self.soup_queue.put(chunk)                
            else:
                writeCSV("LinksWithoutPrice.csv", "ab", host)
                try:
                    findLinks(chunk, "ul", "mainmenu")
                except:
                    print ("Broken Link" + host)
                    writeCSV("BrokenLinks.csv", "ab", host)
                    brokenLinks.append(host)
                    time.sleep(30)                

            self.queue.task_done()

class GetDataURL(threading.Thread):
    getDataUrlLock = threading.Lock()
    def __init__ (self, soup_queue):
        threading.Thread.__init__(self)
        self.soup_queue = soup_queue
    def run(self):
        while True:
            chunk = self.soup_queue.get()
            soup = BeautifulSoup(chunk)
            dataArticle = soup.findAll("tr",{"class":""})
            pagination = soup.findAll("a",{"class":"page"})

            self.getDataUrlLock.acquire()
            f = open(fn + fileName + ".csv", "ab")
            filePrice = csv.writer(f)
            for groupData in dataArticle:
                for articleName in groupData.findAll("a",{"class":"noFloat"}):
                    fullName = articleName.string.encode('utf-8')
                    print (fullName)
                for articlePrice in groupData.findAll("div", {"class":"price"}):
                    if (len(articlePrice) > 1):
                        fullPrice = articlePrice.contents[2].strip()
                    else:
                        fullPrice = articlePrice.get_text().strip()
                    print (fullPrice[:-12])
                    print ('-')*80        

                filePrice.writerow([fullName, fullPrice[:-12]])
            f.close()

            for page in pagination:
                pageLink = page.get('href')
                pageLinks.append('http://www.' + pageLink[1:])
            self.getDataUrlLock.release()

            self.soup_queue.task_done()

def writeCSV(fileName, writeMode, link):
    try:
        brava.acquire()
        f = csv.writer(open(fn + fileName,writeMode))
        f.writerow([link])        
    except IOError as e:
        print (e.message)
    finally:
        brava.release()

def findLinks(chunk, tagName, className):
    soup = BeautifulSoup(chunk)
    mainmenu = soup.findAll(tagName,{"class":className})
    for mm in mainmenu:
        for link in mm.findAll('a'):
            href = link.get('href')
            links.insert(0,href)            
            print (href)
            print ('-')*80

def startMain(links):
    while (links):
        #time.sleep(10)
        threadLinks = links[-10:]        

        print ("Alive Threads: " + str(threading.activeCount()))
        #time.sleep(1)

        for item in range(len(threadLinks)):
            links.pop()

        for i in range(len(threadLinks)):
            tu = TakeURL(queue, soup_queue)
            tu.setDaemon(True)
            tu.start()            

        for host in threadLinks:
            queue.put(host)

        for i in range(len(threadLinks)):
            gdu = GetDataURL(soup_queue)
            gdu.setDaemon(True)
            gdu.start()

        queue.join()
        soup_queue.join()        


if __name__ == "__main__":
    start = time.time()

    httpWeb = urllib2.urlopen(httpLink)
    chunk = httpWeb.read()
    findLinks(chunk, 'li','tab')

        startMain(links)
        pageLinks = list(set(pageLinks))
        startMain(pageLinks)
        startMain(brokenLinks)

    print ('-') * 80
    print ("Seconds: ") % (time.time() - start)
    print ('-') * 80

1 个答案:

答案 0 :(得分:2)

你的线程永远不会返回任何东西,所以它永远不会停止;只是不断运行while循环。而且,由于您为每个链接启动了一个新线程,您最终只会继续添加越来越多的线程,而以前的线程可能没有做任何事情。你基本上不需要queue的方式。正如您所注意到的,这种方法可能会导致大量工作出现问题。

worker = GetDataURL() worker.start()

确实指向GetDataURL.run() ...这是一个无限的while循环。 TakeURL.start()也是如此。

你可以去几条路线

1)只需暂停线程,取消队列并在run定义的 end 处返回结果。这样每个线程有1个任务,返回结果,然后停止。不是最有效但需要最少量的代码修改。

2)在你的startMain中,在while循环之外,启动一组说10个线程(即线程池)。这10个线程将始终运行,而不是为每个链接启动新线程,只需将链接放入队列即可。当一个线程可用时,它将运行队列中的下一个项目。但是你仍然需要管理这些线程的清理工作。

3)您可以更多地重新编写代码,并使用内置函数,如线程池和进程池。我之前发布过流程池:SO MultiProcessing 使用这种方法,您也可以忘记与锁相关的所有混乱。在每个pool.map(或您使用的任何内容)之后,您可以将该大量信息添加到startMain代码中的文件中。清理很多事情。

希望这有点道理。我选择不修改您的代码,因为我认为值得您尝试选项并选择方向。