@FadeTrack
2015-09-03T14:33:49.000000Z
字数 8343
阅读 4891
Python 爬虫
终于迎来了我们可爱的下篇,这一篇主要讲一下 “多线程”、以及 “代理” 的那些事儿。
我们是需要并发处理能力
关于 Python 多线程的文章网上是一搜一大把,然而这个 Python 的多线程是否是伪多线程,这个我还说不准。
今天说的这个“多线程”,准确来说应该是叫做并发计算,(咱们这里不去深究这个并发的真伪)
今天出场的是这个
from multiprocessing.dummy import Pool as ThreadPool
根据字面意思就是 从 multiprocessing.dummy 里面导入 Pool 作为 ThreadPool
老规矩 F1 进去看一下
里面叽叽歪歪的说了一大通。后面还附了个例子。
如果是有兴趣的话可以自己研究一下。
这里只写出最基本的用法,来满足目前的并行需求。
pool = ThreadPool(并行数量)results = pool.map(需要并行的函数, 参数是一个list)pool.close()pool.join()
作用:会迭代这个 list 把 list 中每一项作为参数传入函数中,并调用这个函数。
threading 模块
这里给出一个链接 threading 与 多线程,
里面对class threading.Thread()做了一些说明。
我把他的例子给拷贝一份过来,防止原文以后找不到。
#coding=utf-8import threadingfrom time import ctime,sleepdef music(func):for i in range(2):print "I was listening to %s. %s" %(func,ctime())sleep(1)def move(func):for i in range(2):print "I was at the %s! %s" %(func,ctime())sleep(5)threads = []t1 = threading.Thread(target=music,args=(u'爱情买卖',))threads.append(t1)t2 = threading.Thread(target=move,args=(u'阿凡达',))threads.append(t2)if __name__ == '__main__':for t in threads:t.setDaemon(True)t.start()print "all over %s" %ctime()
import threading首先导入
threading模块,这是使用多线程的前提。
threads = []t1 = threading.Thread(target=music,args=(u'爱情买卖',))threads.append(t1)
创建了
threads数组,创建线程t1,使用threading.Thread()方法,在这个方法中调用music方法target=music,args方法对music进行传参。 把创建好的线程t1装到threads数组中。接着以同样的方式创建线程
t2,并把t2也装到threads数组。
for t in threads:t.setDaemon(True)t.start()
最后通过
for循环遍历数组。(数组被装载了t1和t2两个线程)
setDaemon()
setDaemon(True)将线程声明为守护线程,必须在start()方法调用之前设置,如果不设置为守护线程程序会被无限挂起。子线程启动后,父线程也继续执行下去,当父线程执行完最后一条语句print "all over %s" %ctime()后,没有等待子线程,直接就退出了,同时子线程也一同结束。
start()开始线程活动。
这个例子可以说是简洁明了了,还有另一种做法就是 继承这个 threading.Thread 类,重写里面的几个方法。 __init__ 、run。
这种做法较为复杂,日后有机会再重提。
一种间接访问的方法,可以"隐藏"自己的IP等信息。
详情可以百度得知。
爬虫在爬一些网站时,往往会碰到有些网站会有访问限制,例如一分钟最多只能被同一个IP打开多少次,如果超过一个次数就认定为非人为打开。这个时候可以通过代理来绕过限制。
从免费的代理网站上把 那些地址端口扣下来 组建一张list,使用之。
使用步骤如下:
# 安装代理 步骤
# 1. 选择代理
# 2. 建立代理
# 3. 安装代理
对应到代码:
def change_proxy():# 随机从序列中取出一个元素proxy = random.choice(proxies)# 判断元素是否合理if proxy == None:proxy_support = urllib.request.ProxyHandler({})else:proxy_support = urllib.request.ProxyHandler({'http':proxy})opener = urllib.request.build_opener(proxy_support)opener.addheaders = [('User-Agent',headers['User-Agent'])]urllib.request.install_opener(opener)print('智能切换代理:%s' % ('本机' if proxy==None else proxy))
这里就提到了一个模块 random ,之前也说过,取随机数,random.choice 随机从序列中取出一个元素。
proxy_support = urllib.request.ProxyHandler({'http':proxy})
type(proxy_support)
<class 'urllib.request.ProxyHandler'>
建立代理
urllib.request.build_opener(proxy_support)
安装代理
urllib.request.install_opener(opener)
完成了这个步骤之后,当前 Python 程序就会使用当前安装的代理IP来请求数据了。
写一段代码验证一下。
import urllib.requestimport re,randomproxies = ['113.215.0.130:80',None]headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36'}def change_proxy():# 随机从序列中取出一个元素proxy = random.choice(proxies)# 判断元素是否合理if proxy == None:proxy_support = urllib.request.ProxyHandler({})else:proxy_support = urllib.request.ProxyHandler({'http':proxy})opener = urllib.request.build_opener(proxy_support)opener.addheaders = [('User-Agent',headers['User-Agent'])]urllib.request.install_opener(opener)print('智能切换代理:%s' % ('本机' if proxy==None else proxy))if __name__ == '__main__':# 试图代理一下change_proxy()# 获取一下当前的IP信息response = urllib.request.urlopen('http://1111.ip138.com/ic.asp',timeout = 1000)html = response.read().decode('gbk')regx = '\[(.+?)\]'pat = re.compile(regx)curip = re.search(pat,str(html))print(str(curip.group()))
发现运行结果为 本机IP 和 代理IP 两种结果,再获取一下 免费代理IP列表试试。
def get_proxy():# 使用全局变量,修改之global proxiestry:# 试图获取西刺代理的 IP 列表req = urllib.request.Request('http://www.xicidaili.com/',None,headers)except:print('无法获取代理信息!')returnresponse = urllib.request.urlopen(req)html = response.read().decode('utf-8')p = re.compile(r'''<tr\sclass[^>]*>\s+<td>.+</td>\s+<td>(.*)?</td>\s+<td>(.*)?</td>\s+<td>(.*)?</td>\s+<td>(.*)?</td>\s+<td>(.*)?</td>\s+<td>(.*)?</td>\s+</tr>''',re.VERBOSE)proxy_list = p.findall(html)for each_proxy in proxy_list[1:]:if each_proxy[4] == 'HTTP':proxies.append(each_proxy[0]+':'+each_proxy[1])
获得我们的一个小小的半成品
import urllib.requestimport re,randomproxies = [None]headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36'}def get_proxy():# 使用全局变量,修改之global proxiestry:req = urllib.request.Request('http://www.xicidaili.com/',None,headers)except:print('无法获取代理信息!')returnresponse = urllib.request.urlopen(req)html = response.read().decode('utf-8')p = re.compile(r'''<tr\sclass[^>]*>\s+<td>.+</td>\s+<td>(.*)?</td>\s+<td>(.*)?</td>\s+<td>(.*)?</td>\s+<td>(.*)?</td>\s+<td>(.*)?</td>\s+<td>(.*)?</td>\s+</tr>''',re.VERBOSE)proxy_list = p.findall(html)for each_proxy in proxy_list[1:]:if each_proxy[4] == 'HTTP':proxies.append(each_proxy[0]+':'+each_proxy[1])def change_proxy():# 随机从序列中取出一个元素proxy = random.choice(proxies)# 判断元素是否合理if proxy == None:proxy_support = urllib.request.ProxyHandler({})else:proxy_support = urllib.request.ProxyHandler({'http':proxy})opener = urllib.request.build_opener(proxy_support)opener.addheaders = [('User-Agent',headers['User-Agent'])]urllib.request.install_opener(opener)print('智能切换代理:%s' % ('本机' if proxy==None else proxy))if __name__ == '__main__':# 试图获取列表get_proxy()print('有效代理个数为 : %d' % len(proxies))for i in range(0,10):# 试图代理一下change_proxy()# 获取一下当前的IP信息try:response = urllib.request.urlopen('http://1111.ip138.com/ic.asp',timeout = 5)html = response.read().decode('gbk')regx = '\[(.+?)\]'pat = re.compile(regx)curip = re.search(pat,str(html))print(str(curip.group()))except:print('URL 错误!')pass
现在只需要在我们的爬虫打开 url之前 切换一下IP就好了
change_proxy()
最后贴上我写的刷 CSDN 博客访问量的脚本
# 刷 CSDN 博客访问量import urllib.requestimport re,randomfrom multiprocessing.dummy import Pool as ThreadPooltime_out = 3 # 全局变量 10 秒超时时间count = 0proxies = [None]headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36'}def get_proxy():# 使用全局变量,修改之global proxiestry:req = urllib.request.Request('http://www.xicidaili.com/',None,headers)except:print('无法获取代理信息!')returnresponse = urllib.request.urlopen(req)html = response.read().decode('utf-8')p = re.compile(r'''<tr\sclass[^>]*>\s+<td>.+</td>\s+<td>(.*)?</td>\s+<td>(.*)?</td>\s+<td>(.*)?</td>\s+<td>(.*)?</td>\s+<td>(.*)?</td>\s+<td>(.*)?</td>\s+</tr>''',re.VERBOSE)proxy_list = p.findall(html)for each_proxy in proxy_list[1:]:if each_proxy[4] == 'HTTP':proxies.append(each_proxy[0]+':'+each_proxy[1])def change_proxy():# 随机从序列中取出一个元素proxy = random.choice(proxies)# 判断元素是否合理if proxy == None:proxy_support = urllib.request.ProxyHandler({})else:proxy_support = urllib.request.ProxyHandler({'http':proxy})opener = urllib.request.build_opener(proxy_support)opener.addheaders = [('User-Agent',headers['User-Agent'])]urllib.request.install_opener(opener)print('智能切换代理:%s' % ('本机' if proxy==None else proxy))def get_req(url):# 先伪造一下头部吧,使用字典blog_eader = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 Safari/537.36','Host':'blog.csdn.net','Referer':'http://blog.csdn.net/','GET':url}req = urllib.request.Request(url,headers = blog_eader)return req# 访问 博客def look_blog(url):# 切换一下IPchange_proxy()req = get_req(url)try:urllib.request.urlopen(req,timeout = time_out)except:returnelse:print('访问成功!')# 迭代访问def click_blog(url):for i in range(0,count):if(i == count):breakprint('当前访问 Blog %s 第 %d 次' % (url,i))look_blog(url)# 获取博客的文章链表def get_blog_list(url):req = get_req(url)try:response = urllib.request.urlopen(req,timeout = time_out)except:print('无法挽回的错误')return None# 由于 Csdn 是 utf-8 所以不需要转码html = response.read()# 存储一个正则表达式 规则regx = '<span class="link_title"><a href="(.+?)">'pat = re.compile(regx)# 其实这里 写作 list1 = re.findall('<span class="link_title"><a href="(.+?)">',str(html)) 也是一样的结果blog_list = re.findall(pat,str(html))return blog_listif __name__ == '__main__':global count# 基本参数初始化# 获取代理get_proxy()print('有效代理个数为 : %d' % len(proxies))blogurl = input('输入blog链接:')# 这个地方原本是我的默认输入偷懒用的if len(blogurl) == 0:blogurl = 'http://blog.csdn.net/bkxiaoc/'print('博客地址是:%s' % blogurl)try:count = int(input('输入次数:'))except ValueError:print('参数错误')quit()if count == 0 or count > 999:print('次数过大或过小')quit()print('次数确认为 %d' % count)# 获取 博文 列表,由于测试时我的博文只有一页所以 只能获得一页的列表blog_list = get_blog_list(blogurl + '?viewmode=contents')if len(blog_list) == 0:print('未找到Blog列表')quit()print('启动!!!!!!!!!!!!!!!!!!!!')# 迭代一下 使用多线程index = 0for each_link in blog_list:# 补全头部each_link = 'http://blog.csdn.net' + each_linkblog_list[index] = each_linkindex += 1# 有多少个帖子就开多少个线程的一半 let's gopool = ThreadPool(int(len(blog_list) / 2))results = pool.map(click_blog, blog_list)pool.close()pool.join()print('完成任务!!!!!!!!!!!!!!!!!!!!')
这是在我原来的代码上面拼凑出来的,并没有做什么优化,代码本身依旧存在诸多 BUG,贴在此处仅仅是作为一个简单的使用参考,也算是画上一个句点吧。
如果你有更好的代码,也请你告诉我,我将替换掉上面这个例子。