python多线程爬虫实例-Python爬虫开发【第1篇】【多线程爬虫及案例】

糗事百科爬虫实例:

需求:

使用requests获取页面信息,用XPath / re 做数据提取

获取每个帖子里的用户头像链接、用户姓名、段子内容、点赞次数和评论次数

保存到 json 文件内

#qiushibaike.py

#import urllib

#import re

#import chardet

import requests

from lxml import etree

page = 1

url = 'http://www.qiushibaike.com/8hr/page/' + str(page)

headers = {

'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36',

'Accept-Language': 'zh-CN,zh;q=0.8'}

try:

response = requests.get(url, headers=headers)

resHtml = response.text

html = etree.HTML(resHtml)

result = html.xpath('//div[contains(@id,"qiushi_tag")]')

for site in result:

item = {}

imgUrl = site.xpath('./div/a/img/@src')[0].encode('utf-8')

username = site.xpath('./div/a/@title')[0].encode('utf-8')

#username = site.xpath('.//h2')[0].text

content = site.xpath('.//div[@class="content"]/span')[0].text.strip().encode('utf-8')

# 投票次数

vote = site.xpath('.//i')[0].text

#print site.xpath('.//*[@class="number"]')[0].text

# 评论信息

comments = site.xpath('.//i')[1].text

print imgUrl, username, content, vote, comments

except Exception, e:

print e

Queue(队列对象)

Queue是python中的标准库,可以直接import Queue引用;队列是线程间最常用的交换数据的形式

python下多线程的思考

对于资源,加锁是个重要的环节。因为python原生的list,dict等,都是not thread safe的。而Queue,是线程安全的,因此在满足使用条件下,建议使用队列

初始化: class Queue.Queue(maxsize) FIFO 先进先出

包中的常用方法:

Queue.qsize() 返回队列的大小

Queue.empty() 如果队列为空,返回True,反之False

Queue.full() 如果队列满了,返回True,反之False

Queue.full 与 maxsize 大小对应

Queue.get([block[, timeout]])获取队列,timeout等待时间

创建一个“队列”对象

import Queue

myqueue = Queue.Queue(maxsize = 10)

将一个值放入队列中

myqueue.put(10)

将一个值从队列中取出

myqueue.get()

多线程爬虫示意图:

1277651-20180811215527795-659077354.png

# -*- coding:utf-8 -*-

import requests

from lxml import etree

from Queue import Queue

import threading

import time

import json

class thread_crawl(threading.Thread):

'''

抓取线程类

'''

def __init__(self, threadID, q):

threading.Thread.__init__(self)

self.threadID = threadID

self.q = q

def run(self):

print "Starting " + self.threadID

self.qiushi_spider()

print "Exiting ", self.threadID

def qiushi_spider(self):

# page = 1

while True:

if self.q.empty():

break

else:

page = self.q.get()

print 'qiushi_spider=', self.threadID, ',page=', str(page)

url = 'http://www.qiushibaike.com/8hr/page/' + str(page) + '/'

headers = {

'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36',

'Accept-Language': 'zh-CN,zh;q=0.8'}

# 多次尝试失败结束、防止死循环

timeout = 4

while timeout > 0:

timeout -= 1

try:

content = requests.get(url, headers=headers)

data_queue.put(content.text)

break

except Exception, e:

print 'qiushi_spider', e

if timeout < 0:

print 'timeout', url

class Thread_Parser(threading.Thread):

'''

页面解析类;

'''

def __init__(self, threadID, queue, lock, f):

threading.Thread.__init__(self)

self.threadID = threadID

self.queue = queue

self.lock = lock

self.f = f

def run(self):

print 'starting ', self.threadID

global total, exitFlag_Parser

while not exitFlag_Parser:

try:

'''

调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为True。

如果队列为空且block为True,get()就使调用线程暂停,直至有项目可用。

如果队列为空且block为False,队列将引发Empty异常。

'''

item = self.queue.get(False)

if not item:

pass

self.parse_data(item)

self.queue.task_done()

print 'Thread_Parser=', self.threadID, ',total=', total

except:

pass

print 'Exiting ', self.threadID

def parse_data(self, item):

'''

解析网页函数

:param item: 网页内容

:return:

'''

global total

try:

html = etree.HTML(item)

result = html.xpath('//div[contains(@id,"qiushi_tag")]')

for site in result:

try:

imgUrl = site.xpath('.//img/@src')[0]

title = site.xpath('.//h2')[0].text

content = site.xpath('.//div[@class="content"]/span')[0].text.strip()

vote = None

comments = None

try:

vote = site.xpath('.//i')[0].text

comments = site.xpath('.//i')[1].text

except:

pass

result = {

'imgUrl': imgUrl,

'title': title,

'content': content,

'vote': vote,

'comments': comments,

}

with self.lock:

# print 'write %s' % json.dumps(result)

self.f.write(json.dumps(result, ensure_ascii=False).encode('utf-8') + " ")

except Exception, e:

print 'site in result', e

except Exception, e:

print 'parse_data', e

with self.lock:

total += 1

data_queue = Queue()

exitFlag_Parser = False

lock = threading.Lock()

total = 0

def main():

output = open('qiushibaike.json', 'a')

#初始化网页页码page从1-10个页面

pageQueue = Queue(50)

for page in range(1, 11):

pageQueue.put(page)

#初始化采集线程

crawlthreads = []

crawlList = ["crawl-1", "crawl-2", "crawl-3"]

for threadID in crawlList:

thread = thread_crawl(threadID, pageQueue)

thread.start()

crawlthreads.append(thread)

#初始化解析线程parserList

parserthreads = []

parserList = ["parser-1", "parser-2", "parser-3"]

#分别启动parserList

for threadID in parserList:

thread = Thread_Parser(threadID, data_queue, lock, output)

thread.start()

parserthreads.append(thread)

# 等待队列清空

while not pageQueue.empty():

pass

# 等待所有线程完成

for t in crawlthreads:

t.join()

while not data_queue.empty():

pass

# 通知线程是时候退出

global exitFlag_Parser

exitFlag_Parser = True

for t in parserthreads:

t.join()

print "Exiting Main Thread"

with lock:

output.close()

if __name__ == '__main__':

main()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个简单的Python多线程爬虫示例: ```python import requests import threading from queue import Queue # 定义一个全局变量用来存储爬取到的数据 data = [] # 定义一个线程锁,防止多个线程同时写入数据导致数据错乱 lock = threading.Lock() # 定义一个队列,用来存储待爬取的URL url_queue = Queue() # 定义一个爬取线程类 class CrawlerThread(threading.Thread): def __init__(self, url_queue): super().__init__() self.url_queue = url_queue def run(self): while True: # 从队列中获取一个URL url = self.url_queue.get() try: # 发送请求并解析响应数据 response = requests.get(url) content = response.text # 对响应数据进行处理(此处省略) # ... # 将处理后的数据存入全局变量 with lock: data.append(processed_data) # 标记该URL已被处理 self.url_queue.task_done() except Exception as e: print(f"Error occurred while crawling {url}: {e}") # 如果发生错误,将该URL重新放回队列 self.url_queue.put(url) self.url_queue.task_done() # 定义一个入口函数 def main(): # 初始化待爬取的URL列表 urls = ["http://www.example.com/page{}".format(i) for i in range(1, 11)] # 将URL列表添加到队列中 for url in urls: url_queue.put(url) # 创建多个爬取线程并启动 for i in range(5): t = CrawlerThread(url_queue) t.start() # 阻塞主线程,直到所有URL都被处理完毕 url_queue.join() # 输出爬取结果 print(data) if __name__ == '__main__': main() ``` 以上示例中,我们定义了一个`CrawlerThread`类来表示爬取线程,定义了一个`url_queue`队列来存储待爬取的URL,定义了一个`data`列表来存储爬取到的数据,以及定义了一个`lock`线程锁来保证多个线程访问`data`时不会出现数据错乱的情况。 在`main`函数中,我们首先将待爬取的URL列表添加到`url_queue`队列中,然后创建多个`CrawlerThread`实例并启动它们。最后,我们使用`url_queue.join()`方法来阻塞主线程,直到所有的URL都被处理完毕。 在`CrawlerThread`类的`run`方法中,我们使用`self.url_queue.get()`方法从`url_queue`队列中获取一个URL,然后发送请求并解析响应数据。如果处理过程中出现错误,我们将该URL重新放回队列中,以便后续线程重新处理。如果处理成功,我们将处理后的数据存入`data`列表中,并使用`self.url_queue.task_done()`方法标记该URL已被处理完毕。 需要注意的是,在多线程爬虫中,如果爬取的网站对IP访问频率有限制,可能会导致IP被封禁的情况。因此,在实际应用中,我们需要合理设置线程数量和请求频率,以避免被封禁的情况发生。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值