@FadeTrack
2015-09-03T22:33:49.000000Z
字数 8343
阅读 4673
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-8
import threading
from time import ctime,sleep
def 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.request
import re,random
proxies = ['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 proxies
try:
# 试图获取西刺代理的 IP 列表
req = urllib.request.Request('http://www.xicidaili.com/',None,headers)
except:
print('无法获取代理信息!')
return
response = 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.request
import re,random
proxies = [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 proxies
try:
req = urllib.request.Request('http://www.xicidaili.com/',None,headers)
except:
print('无法获取代理信息!')
return
response = 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.request
import re,random
from multiprocessing.dummy import Pool as ThreadPool
time_out = 3 # 全局变量 10 秒超时时间
count = 0
proxies = [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 proxies
try:
req = urllib.request.Request('http://www.xicidaili.com/',None,headers)
except:
print('无法获取代理信息!')
return
response = 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):
# 切换一下IP
change_proxy()
req = get_req(url)
try:
urllib.request.urlopen(req,timeout = time_out)
except:
return
else:
print('访问成功!')
# 迭代访问
def click_blog(url):
for i in range(0,count):
if(i == count):
break
print('当前访问 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_list
if __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 = 0
for each_link in blog_list:
# 补全头部
each_link = 'http://blog.csdn.net' + each_link
blog_list[index] = each_link
index += 1
# 有多少个帖子就开多少个线程的一半 let's go
pool = ThreadPool(int(len(blog_list) / 2))
results = pool.map(click_blog, blog_list)
pool.close()
pool.join()
print('完成任务!!!!!!!!!!!!!!!!!!!!')
这是在我原来的代码上面拼凑出来的,并没有做什么优化,代码本身依旧存在诸多 BUG
,贴在此处仅仅是作为一个简单的使用参考,也算是画上一个句点吧。
如果你有更好的代码,也请你告诉我,我将替换掉上面这个例子。