您的位置:首页 > 编程语言 > Python开发

Python爬虫入门 《下》

2015-09-03 22:41 459 查看

楔子

终于迎来了我们可爱的下篇,这一篇主要讲一下 “多线程”、以及 “代理” 的那些事儿。

“多线程”

为何需要使用多线程?

我们是需要并发处理能力

哪里获取多线程的知识

关于
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
,贴在此处仅仅是作为一个简单的使用参考,也算是画上一个句点吧。

如果你有更好的代码,也请你告诉我,我将替换掉上面这个例子。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: