[关闭]
@FadeTrack 2015-09-03T22:33:49.000000Z 字数 8343 阅读 4680

Python爬虫入门 《下》

Python 爬虫


楔子

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

“多线程”

为何需要使用多线程?

我们是需要并发处理能力

哪里获取多线程的知识

关于 Python 多线程的文章网上是一搜一大把,然而这个 Python 的多线程是否是伪多线程,这个我还说不准。

今天讲什么?

今天说的这个“多线程”,准确来说应该是叫做并发计算,(咱们这里不去深究这个并发的真伪)

今天出场的是这个

  1. from multiprocessing.dummy import Pool as ThreadPool

根据字面意思就是 从 multiprocessing.dummy 里面导入 Pool 作为 ThreadPool

老规矩 F1 进去看一下

里面叽叽歪歪的说了一大通。后面还附了个例子。
如果是有兴趣的话可以自己研究一下。

这里只写出最基本的用法,来满足目前的并行需求。

  1. pool = ThreadPool(并行数量)
  2. results = pool.map(需要并行的函数, 参数是一个list)
  3. pool.close()
  4. pool.join()

作用:会迭代这个 list 把 list 中每一项作为参数传入函数中,并调用这个函数。

再说另一种(拓展)

threading 模块
这里给出一个链接 threading 与 多线程,
里面对class threading.Thread()做了一些说明。
我把他的例子给拷贝一份过来,防止原文以后找不到。

  1. #coding=utf-8
  2. import threading
  3. from time import ctime,sleep
  4. def music(func):
  5. for i in range(2):
  6. print "I was listening to %s. %s" %(func,ctime())
  7. sleep(1)
  8. def move(func):
  9. for i in range(2):
  10. print "I was at the %s! %s" %(func,ctime())
  11. sleep(5)
  12. threads = []
  13. t1 = threading.Thread(target=music,args=(u'爱情买卖',))
  14. threads.append(t1)
  15. t2 = threading.Thread(target=move,args=(u'阿凡达',))
  16. threads.append(t2)
  17. if __name__ == '__main__':
  18. for t in threads:
  19. t.setDaemon(True)
  20. t.start()
  21. print "all over %s" %ctime()

import threading

首先导入threading 模块,这是使用多线程的前提。

  1. threads = []
  2. t1 = threading.Thread(target=music,args=(u'爱情买卖',))
  3. threads.append(t1)

  创建了threads数组,创建线程t1,使用threading.Thread()方法,在这个方法中调用music方法target=musicargs方法对music进行传参。 把创建好的线程t1装到threads数组中。

  接着以同样的方式创建线程t2,并把t2也装到threads数组。

  1. for t in threads:
  2.   t.setDaemon(True)
  3.   t.start()

最后通过for循环遍历数组。(数组被装载了t1t2两个线程)

setDaemon()

  setDaemon(True)将线程声明为守护线程,必须在start() 方法调用之前设置,如果不设置为守护线程程序会被无限挂起。子线程启动后,父线程也继续执行下去,当父线程执行完最后一条语句print "all over %s" %ctime()后,没有等待子线程,直接就退出了,同时子线程也一同结束。

start()

开始线程活动。

这个例子可以说是简洁明了了,还有另一种做法就是 继承这个 threading.Thread 类,重写里面的几个方法。 __init__run
这种做法较为复杂,日后有机会再重提。

代理

什么是代理?

一种间接访问的方法,可以"隐藏"自己的IP等信息。

详情可以百度得知。

为何使用代理?

爬虫在爬一些网站时,往往会碰到有些网站会有访问限制,例如一分钟最多只能被同一个IP打开多少次,如果超过一个次数就认定为非人为打开。这个时候可以通过代理来绕过限制。

如何使用代理?

从免费的代理网站上把 那些地址端口扣下来 组建一张list,使用之。

使用步骤如下:

# 安装代理 步骤
# 1. 选择代理
# 2. 建立代理
# 3. 安装代理

对应到代码:

  1. def change_proxy():
  2. # 随机从序列中取出一个元素
  3. proxy = random.choice(proxies)
  4. # 判断元素是否合理
  5. if proxy == None:
  6. proxy_support = urllib.request.ProxyHandler({})
  7. else:
  8. proxy_support = urllib.request.ProxyHandler({'http':proxy})
  9. opener = urllib.request.build_opener(proxy_support)
  10. opener.addheaders = [('User-Agent',headers['User-Agent'])]
  11. urllib.request.install_opener(opener)
  12. 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来请求数据了。

写一段代码验证一下。

  1. import urllib.request
  2. import re,random
  3. proxies = ['113.215.0.130:80',None]
  4. 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'}
  5. def change_proxy():
  6. # 随机从序列中取出一个元素
  7. proxy = random.choice(proxies)
  8. # 判断元素是否合理
  9. if proxy == None:
  10. proxy_support = urllib.request.ProxyHandler({})
  11. else:
  12. proxy_support = urllib.request.ProxyHandler({'http':proxy})
  13. opener = urllib.request.build_opener(proxy_support)
  14. opener.addheaders = [('User-Agent',headers['User-Agent'])]
  15. urllib.request.install_opener(opener)
  16. print('智能切换代理:%s' % ('本机' if proxy==None else proxy))
  17. if __name__ == '__main__':
  18. # 试图代理一下
  19. change_proxy()
  20. # 获取一下当前的IP信息
  21. response = urllib.request.urlopen('http://1111.ip138.com/ic.asp',timeout = 1000)
  22. html = response.read().decode('gbk')
  23. regx = '\[(.+?)\]'
  24. pat = re.compile(regx)
  25. curip = re.search(pat,str(html))
  26. print(str(curip.group()))

发现运行结果为 本机IP 和 代理IP 两种结果,再获取一下 免费代理IP列表试试。

  1. def get_proxy():
  2. # 使用全局变量,修改之
  3. global proxies
  4. try:
  5. # 试图获取西刺代理的 IP 列表
  6. req = urllib.request.Request('http://www.xicidaili.com/',None,headers)
  7. except:
  8. print('无法获取代理信息!')
  9. return
  10. response = urllib.request.urlopen(req)
  11. html = response.read().decode('utf-8')
  12. p = re.compile(r'''<tr\sclass[^>]*>\s+
  13. <td>.+</td>\s+
  14. <td>(.*)?</td>\s+
  15. <td>(.*)?</td>\s+
  16. <td>(.*)?</td>\s+
  17. <td>(.*)?</td>\s+
  18. <td>(.*)?</td>\s+
  19. <td>(.*)?</td>\s+
  20. </tr>''',re.VERBOSE)
  21. proxy_list = p.findall(html)
  22. for each_proxy in proxy_list[1:]:
  23. if each_proxy[4] == 'HTTP':
  24. proxies.append(each_proxy[0]+':'+each_proxy[1])

获得我们的一个小小的半成品

  1. import urllib.request
  2. import re,random
  3. proxies = [None]
  4. 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'}
  5. def get_proxy():
  6. # 使用全局变量,修改之
  7. global proxies
  8. try:
  9. req = urllib.request.Request('http://www.xicidaili.com/',None,headers)
  10. except:
  11. print('无法获取代理信息!')
  12. return
  13. response = urllib.request.urlopen(req)
  14. html = response.read().decode('utf-8')
  15. p = re.compile(r'''<tr\sclass[^>]*>\s+
  16. <td>.+</td>\s+
  17. <td>(.*)?</td>\s+
  18. <td>(.*)?</td>\s+
  19. <td>(.*)?</td>\s+
  20. <td>(.*)?</td>\s+
  21. <td>(.*)?</td>\s+
  22. <td>(.*)?</td>\s+
  23. </tr>''',re.VERBOSE)
  24. proxy_list = p.findall(html)
  25. for each_proxy in proxy_list[1:]:
  26. if each_proxy[4] == 'HTTP':
  27. proxies.append(each_proxy[0]+':'+each_proxy[1])
  28. def change_proxy():
  29. # 随机从序列中取出一个元素
  30. proxy = random.choice(proxies)
  31. # 判断元素是否合理
  32. if proxy == None:
  33. proxy_support = urllib.request.ProxyHandler({})
  34. else:
  35. proxy_support = urllib.request.ProxyHandler({'http':proxy})
  36. opener = urllib.request.build_opener(proxy_support)
  37. opener.addheaders = [('User-Agent',headers['User-Agent'])]
  38. urllib.request.install_opener(opener)
  39. print('智能切换代理:%s' % ('本机' if proxy==None else proxy))
  40. if __name__ == '__main__':
  41. # 试图获取列表
  42. get_proxy()
  43. print('有效代理个数为 : %d' % len(proxies))
  44. for i in range(0,10):
  45. # 试图代理一下
  46. change_proxy()
  47. # 获取一下当前的IP信息
  48. try:
  49. response = urllib.request.urlopen('http://1111.ip138.com/ic.asp',timeout = 5)
  50. html = response.read().decode('gbk')
  51. regx = '\[(.+?)\]'
  52. pat = re.compile(regx)
  53. curip = re.search(pat,str(html))
  54. print(str(curip.group()))
  55. except:
  56. print('URL 错误!')
  57. pass

现在只需要在我们的爬虫打开 url之前 切换一下IP就好了

change_proxy()

最后贴上我写的刷 CSDN 博客访问量的脚本

  1. # 刷 CSDN 博客访问量
  2. import urllib.request
  3. import re,random
  4. from multiprocessing.dummy import Pool as ThreadPool
  5. time_out = 3 # 全局变量 10 秒超时时间
  6. count = 0
  7. proxies = [None]
  8. 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'}
  9. def get_proxy():
  10. # 使用全局变量,修改之
  11. global proxies
  12. try:
  13. req = urllib.request.Request('http://www.xicidaili.com/',None,headers)
  14. except:
  15. print('无法获取代理信息!')
  16. return
  17. response = urllib.request.urlopen(req)
  18. html = response.read().decode('utf-8')
  19. p = re.compile(r'''<tr\sclass[^>]*>\s+
  20. <td>.+</td>\s+
  21. <td>(.*)?</td>\s+
  22. <td>(.*)?</td>\s+
  23. <td>(.*)?</td>\s+
  24. <td>(.*)?</td>\s+
  25. <td>(.*)?</td>\s+
  26. <td>(.*)?</td>\s+
  27. </tr>''',re.VERBOSE)
  28. proxy_list = p.findall(html)
  29. for each_proxy in proxy_list[1:]:
  30. if each_proxy[4] == 'HTTP':
  31. proxies.append(each_proxy[0]+':'+each_proxy[1])
  32. def change_proxy():
  33. # 随机从序列中取出一个元素
  34. proxy = random.choice(proxies)
  35. # 判断元素是否合理
  36. if proxy == None:
  37. proxy_support = urllib.request.ProxyHandler({})
  38. else:
  39. proxy_support = urllib.request.ProxyHandler({'http':proxy})
  40. opener = urllib.request.build_opener(proxy_support)
  41. opener.addheaders = [('User-Agent',headers['User-Agent'])]
  42. urllib.request.install_opener(opener)
  43. print('智能切换代理:%s' % ('本机' if proxy==None else proxy))
  44. def get_req(url):
  45. # 先伪造一下头部吧,使用字典
  46. blog_eader = {
  47. 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 Safari/537.36',
  48. 'Host':'blog.csdn.net',
  49. 'Referer':'http://blog.csdn.net/',
  50. 'GET':url
  51. }
  52. req = urllib.request.Request(url,headers = blog_eader)
  53. return req
  54. # 访问 博客
  55. def look_blog(url):
  56. # 切换一下IP
  57. change_proxy()
  58. req = get_req(url)
  59. try:
  60. urllib.request.urlopen(req,timeout = time_out)
  61. except:
  62. return
  63. else:
  64. print('访问成功!')
  65. # 迭代访问
  66. def click_blog(url):
  67. for i in range(0,count):
  68. if(i == count):
  69. break
  70. print('当前访问 Blog %s 第 %d 次' % (url,i))
  71. look_blog(url)
  72. # 获取博客的文章链表
  73. def get_blog_list(url):
  74. req = get_req(url)
  75. try:
  76. response = urllib.request.urlopen(req,timeout = time_out)
  77. except:
  78. print('无法挽回的错误')
  79. return None
  80. # 由于 Csdn 是 utf-8 所以不需要转码
  81. html = response.read()
  82. # 存储一个正则表达式 规则
  83. regx = '<span class="link_title"><a href="(.+?)">'
  84. pat = re.compile(regx)
  85. # 其实这里 写作 list1 = re.findall('<span class="link_title"><a href="(.+?)">',str(html)) 也是一样的结果
  86. blog_list = re.findall(pat,str(html))
  87. return blog_list
  88. if __name__ == '__main__':
  89. global count
  90. # 基本参数初始化
  91. # 获取代理
  92. get_proxy()
  93. print('有效代理个数为 : %d' % len(proxies))
  94. blogurl = input('输入blog链接:')
  95. # 这个地方原本是我的默认输入偷懒用的
  96. if len(blogurl) == 0:
  97. blogurl = 'http://blog.csdn.net/bkxiaoc/'
  98. print('博客地址是:%s' % blogurl)
  99. try:
  100. count = int(input('输入次数:'))
  101. except ValueError:
  102. print('参数错误')
  103. quit()
  104. if count == 0 or count > 999:
  105. print('次数过大或过小')
  106. quit()
  107. print('次数确认为 %d' % count)
  108. # 获取 博文 列表,由于测试时我的博文只有一页所以 只能获得一页的列表
  109. blog_list = get_blog_list(blogurl + '?viewmode=contents')
  110. if len(blog_list) == 0:
  111. print('未找到Blog列表')
  112. quit()
  113. print('启动!!!!!!!!!!!!!!!!!!!!')
  114. # 迭代一下 使用多线程
  115. index = 0
  116. for each_link in blog_list:
  117. # 补全头部
  118. each_link = 'http://blog.csdn.net' + each_link
  119. blog_list[index] = each_link
  120. index += 1
  121. # 有多少个帖子就开多少个线程的一半 let's go
  122. pool = ThreadPool(int(len(blog_list) / 2))
  123. results = pool.map(click_blog, blog_list)
  124. pool.close()
  125. pool.join()
  126. print('完成任务!!!!!!!!!!!!!!!!!!!!')

这是在我原来的代码上面拼凑出来的,并没有做什么优化,代码本身依旧存在诸多 BUG,贴在此处仅仅是作为一个简单的使用参考,也算是画上一个句点吧。

如果你有更好的代码,也请你告诉我,我将替换掉上面这个例子。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注