鱼C论坛

 找回密码
 立即注册
查看: 20177|回复: 92

[作品展示] 爬虫IP被封的对策,讨论一下。附爬虫源码。

[复制链接]
回帖奖励 50 鱼币 回复本帖可获得 5 鱼币奖励! 每人限 1 次(中奖概率 50%)
发表于 2015-5-21 02:37:30 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 Reed 于 2015-5-21 02:42 编辑

现在我用的方法只有一个就是代理IP,去免费代理的网站上先爬下来IP,然后再使用
但是可用率太低,速度也不理想,有什么办法更好的解决这问题么?
鱼油们~~(代理IP可用率低,目前还爬不下来全部数据,现在也是在测试)

下面的代码,主要用了队列和线程,实现类似 消费者&生产者的问题。
写的太丑,实现原理懂的少,如有优化方案还望告知,交流才是第一生产力。
  1. # coding: utf-8
  2. __author__ = 'Reed'

  3. import urllib.request
  4. import queue
  5. import threading
  6. from bs4 import BeautifulSoup
  7. from pandas import Series
  8. # import mysql.connector
  9. from io import BytesIO
  10. import gzip
  11. import numpy


  12. '''
  13. class Desc(threading.Thread):
  14.     def __init__(self, q_url, q_proxy):
  15.         threading.Thread.__init__(self)
  16.         self.q_url = q_url
  17.         self.q_proxy = q_proxy
  18.         self.flag = True

  19.     def run(self):
  20.         while 1:
  21.             if not self.q_proxy.empty() and not self.q_url.empty():
  22.                 url = self.q_url.get()
  23.                 rqt = urllib.request.Request(
  24.                     url=url,
  25.                     headers={'User-Agent': 'Mozilla/5.0'})

  26.                 while 1:
  27.                     if self.flag and not self.q_proxy.empty():
  28.                         addr = self.q_proxy.get()
  29.                     print(addr, self.name, url)
  30.                     proxy_handler = urllib.request.ProxyHandler({'http': addr})
  31.                     opener = urllib.request.build_opener(proxy_handler)
  32.                     try:
  33.                         page = opener.open(rqt, timeout=4).read().decode('utf-8')
  34.                         print('%s read DESCOK!' % self.name)
  35.                         self.flag = False
  36.                         break
  37.                     except:
  38.                         print('%s read DESCFALSE' % self.name)
  39.                         self.flag = True
  40.                         q_proxy.task_done()
  41.                         continue

  42.                 soup = BeautifulSoup(page)

  43.                 info = soup.select('#info')[0].text.encode('gbk', 'ignore').decode('gbk').replace(' ', '').replace('/', '').split('\n')
  44.                 infor = list(filter(lambda x: x, info))
  45.                 dct = dict()
  46.                 for i in range(len(infor)):
  47.                     if infor[i].endswith(':'):
  48.                         dct[infor[i][:-1]] = infor[i+1]
  49.                     elif ':' in infor[i]:
  50.                         k = infor[i].split(':')[0]
  51.                         dct[k] = ''.join(infor[i].split(':')[1:])

  52.                 rate = soup.select('.rating_wrap')[0].select('strong')[0].text.replace(' ', '').replace('\n', '')
  53.                 raters = soup.select('.rating_wrap')[0].select('a')[0].text[:-3]
  54.                 title = soup.title.text.encode('gbk', 'ignore').decode('gbk')[:-5]

  55.                 dct['评分'] = rate
  56.                 dct['评分人数'] = raters
  57.                 dct['书名'] = title

  58.                 data = Series(dct, index=['书名', '作者', '译者', '评分', '评分人数', '定价', '出版社', '出版年', '页数', 'ISBN', '装帧', '副标题', '丛书', '原作名'])
  59.                 desc_book = list(data.replace(numpy.nan, ''))

  60.                 q_desc.put(desc_book)
  61.                 q_url.task_done()
  62. '''
  63. # 上面的Desc(注释掉了)和Book共用一个IP代理队列,其他速度均正常,只有Desc慢很多,有可能是资源竞争搞的,嗯,我猜
  64. # 下面的Desc没有用代理,用本机访问,速度果然就恢复正常了,然后...就被封了
  65. # 应该IP代理队列一分为二,效果应该会好,(免费代理,可用率惊的低人啊)


  66. # 从每本书的详情主页,拿出图书信息,并格式化数据
  67. class Desc(threading.Thread):
  68.     def __init__(self, q_url):
  69.         threading.Thread.__init__(self)
  70.         self.q_url = q_url
  71.         self.flag = True

  72.     def run(self):
  73.         while 1:
  74.             if not self.q_url.empty():
  75.                 url = self.q_url.get()
  76.                 rqt = urllib.request.Request(
  77.                     url=url,
  78.                     headers={'User-Agent': 'Mozilla/5.0'})

  79.                 while 1:
  80.                     opener = urllib.request.build_opener()
  81.                     try:
  82.                         page = opener.open(rqt, timeout=4).read().decode('utf-8')
  83.                         self.flag = False
  84.                         break
  85.                     except:
  86.                         self.flag = True
  87.                         continue

  88.                 # 网页解析,而且雷特别多,比如 · 这个东西GBK无法编码,虽然utf8编码存到DB里,取的时候也可以修正,但是别扭
  89.                 # douban的html代码搞的有点复杂,所以格式化费劲了点,各种replace
  90.                 soup = BeautifulSoup(page)
  91.                 info = soup.select('#info')[0].text.encode('gbk', 'ignore').decode('gbk').replace(' ', '').replace('/', '').split('\n')
  92.                 infor = list(filter(lambda x: x, info))

  93.                 # 这个里就是在格式化数据,存到字典里,为后面pandas.Series格式化数据做好准备
  94.                 # 遗憾的是译者有多个的时候,无法全部取出
  95.                 dct = dict()
  96.                 for i in range(len(infor)):
  97.                     if infor[i].endswith(':'):
  98.                         dct[infor[i][:-1]] = infor[i+1]
  99.                     elif ':' in infor[i]:
  100.                         k = infor[i].split(':')[0]
  101.                         dct[k] = ''.join(infor[i].split(':')[1:])

  102.                 # 取评分,评分人数,书名是上面忘了取
  103.                 rate = soup.select('.rating_wrap')[0].select('strong')[0].text.replace(' ', '').replace('\n', '')
  104.                 raters = soup.select('.rating_wrap')[0].select('a')[0].text[:-3]
  105.                 title = soup.title.text.encode('gbk', 'ignore').decode('gbk')[:-5]
  106.                 dct['评分'] = rate
  107.                 dct['评分人数'] = raters
  108.                 dct['书名'] = title

  109.                 # pandas.Series严格的按照index的顺序,排列dict.values(),空白处会置NAN(numpy.nan),再次replace
  110.                 data = Series(dct, index=['书名', '作者', '译者', '评分', '评分人数', '定价', '出版社', '出版年', '页数', 'ISBN', '装帧', '副标题', '丛书', '原作名'])
  111.                 desc_book = list(data.replace(numpy.nan, ''))

  112.                 q_desc.put(desc_book)
  113.                 q_url.task_done()


  114. # 从q_page队列里,取每一页的url然后获取每本书的url
  115. class Book(threading.Thread):
  116.     def __init__(self, q_page, q_proxy):
  117.         threading.Thread.__init__(self)
  118.         self.q_page = q_page
  119.         self.q_proxy = q_proxy
  120.         self.sign = True

  121.     def run(self):
  122.         while 1:
  123.             if not self.q_page.empty() and not self.q_proxy.empty():
  124.                 page_url = self.q_page.get()
  125.                 rqt = urllib.request.Request(
  126.                     url=page_url,
  127.                     headers={'User-Agent': 'Mozilla/5.0'})

  128.                 # 内层while做到了,如果代理可用,那么下一次换url,不换代理
  129.                 # 如果代理不可用,下一次换代理,不换url
  130.                 while 1:
  131.                     if self.sign and not self.q_proxy.empty():
  132.                         addr = self.q_proxy.get()
  133.                     proxy_handler = urllib.request.ProxyHandler({'http': addr})
  134.                     opener = urllib.request.build_opener(proxy_handler)
  135.                     try:
  136.                         book_page = opener.open(rqt, timeout=4).read().decode('utf-8')
  137.                         self.sign = False
  138.                         break
  139.                     except:
  140.                         self.sign = True
  141.                         q_proxy.task_done()
  142.                         continue

  143.                 # BeautifulSoup脑残用法
  144.                 soup = BeautifulSoup(book_page)
  145.                 for dd in soup.select('dd'):
  146.                     if dd.select('a'):
  147.                         href = dd.select('a')[0].get('href')
  148.                         q_url.put(href)
  149.                 q_page.task_done()


  150. # 将格式化的数据,存到DB里
  151. class DB(threading.Thread):
  152.     def __init__(self, q_desc):
  153.         threading.Thread.__init__(self)
  154.         self.q_desc = q_desc

  155.     def run(self):
  156.         while 1:
  157.             desc = q_desc.get()
  158.             print(self.name)
  159.             # con = mysql.connector.connect(user='root', password='password', host='127.0.0.1', database='douban')
  160.             print('inset into table values %s' % ('/'.join(desc)))
  161.             q_desc.task_done()

  162. # 以上3个class均开启多线程, 与queue队列合作,不用过度关心锁等问题


  163. # 正式开启线程爬book information之前,先爬好代理IP,放到队列
  164. def get_proxy(proxy_url_lst):

  165.     for each_url in proxy_url_lst:
  166.         rqst = urllib.request.Request(each_url)
  167.         rqst.add_header('User-Agent', 'Mozilla/5.0')

  168.         # 这里response header返回的Content-Encoding是gzip,google到的方法是要解压
  169.         # 其他网站有的也是这样,但是并不用解压,不明原因
  170.         try:
  171.             response = urllib.request.urlopen(rqst)
  172.             if response.info().get('Content-Encoding') == 'gzip':
  173.                 buf = BytesIO(response.read())
  174.                 f = gzip.GzipFile(fileobj=buf)
  175.                 each_page = f.read()
  176.             else:
  177.                 each_page = response.read()
  178.         except urllib.request.URLError as r:
  179.             print(r.reason)

  180.         proxy_soup = BeautifulSoup(each_page)
  181.         trs = proxy_soup.select('tbody > tr')

  182.         for tr in trs:
  183.             td = tr.find_all('td')
  184.             proxy_addr = td[0].text + ':' + td[1].text
  185.             q_proxy.put(proxy_addr)


  186. def main():
  187.     proxy_url_list = ['http://www.kuaidaili.com/free/intr/' + str(i) for i in range(1, 30)]
  188.     get_proxy(proxy_url_list)

  189.     page_url = ['http://www.douban.com/tag/%E5%B0%8F%E8%AF%B4/book?start=' + str(n) for n in range(0, 71985, 15)]
  190.     for each in page_url:
  191.         q_page.put(each)

  192.     # 每项任务5个线程
  193.     for i in range(5):
  194.         book = Book(q_page, q_proxy)
  195.         book.setDaemon(True)
  196.         book.start()

  197.         desc = Desc(q_url)
  198.         desc.setDaemon(True)
  199.         desc.start()

  200.         db = DB(q_desc)
  201.         db.setDaemon(True)
  202.         db.start()


  203. if __name__ == '__main__':
  204.     q_url = queue.Queue()       # 每本书的url
  205.     q_page = queue.Queue()      # 每页15本书概况的page url, tag:小说
  206.     q_desc = queue.Queue()      # 每本书的详细描述信息(list)
  207.     q_proxy = queue.Queue()     # 从代理IP网站爬下来的IP地址和端口,'127.0.0.1:8888'

  208.     main()
  209.     q_desc.join()
  210.     print('Done!')
复制代码




本帖被以下淘专辑推荐:

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2015-5-21 09:45:49 | 显示全部楼层
谢谢分享啊!!!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-5-21 14:37:38 | 显示全部楼层
问题果然有点严重,还是代理IP的问题,不代理的话必定会被封,根本爬不下来多少就被封
如果用,质量确实太低,效果不明显
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2015-5-24 20:15:32 | 显示全部楼层
看知乎上说他是申请好多免费的代理服务器。。。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-5-24 20:56:22 | 显示全部楼层
ghvn7777 发表于 2015-5-24 20:15
看知乎上说他是申请好多免费的代理服务器。。。

用了3K个,效果也不好,可能是douban防爬虫做的比较好,在其他网页获取的速度还有可用度就好很多,这个有点... 也可能是代码不够火候
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2015-5-27 17:09:47 | 显示全部楼层
我怎么没有奖励:cry
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2015-5-27 19:40:07 | 显示全部楼层
学习学习:lol:
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2015-5-27 22:41:03 | 显示全部楼层

回帖奖励 +5 鱼币

学习学习
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2015-8-4 10:36:49 | 显示全部楼层
正是我想学习的内容
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2015-8-4 10:39:17 | 显示全部楼层
好长啊,,,看了一会已经晕了。。表示很多都看不懂啊。。。看来需要学习的还很多
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2015-8-7 10:24:37 | 显示全部楼层
我想把微博用爬删掉 求指点啊
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2015-8-7 11:50:21 | 显示全部楼层
学习学习
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2015-8-7 16:20:17 | 显示全部楼层
学习学习哈哈哈:lol:
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2015-8-7 19:36:23 | 显示全部楼层

回帖奖励 +5 鱼币

鱼币先拿来
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-8-7 22:36:37 | 显示全部楼层
woshidamowang24 发表于 2015-8-7 10:24
我想把微博用爬删掉 求指点啊

1、微博应该有公共的API,试试可以不,就是向服务器发出一个命令,然后就删除了
2、自己在浏览器的反复操作,看看是向什么服务器 发出了什么命令,然后再用Python模拟
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2015-8-8 12:49:42 | 显示全部楼层

回帖奖励 +5 鱼币

力顶楼主发帖。。。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2015-8-8 13:53:45 | 显示全部楼层

回帖奖励 +5 鱼币

谢谢分享!!!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2015-8-8 14:08:14 | 显示全部楼层
Reed 发表于 2015-8-7 22:36
1、微博应该有公共的API,试试可以不,就是向服务器发出一个命令,然后就删除了
2、自己在浏览器的反复 ...

好谢谢
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2015-8-8 23:25:59 | 显示全部楼层

回帖奖励 +5 鱼币

我的天这一大串代码,感觉好厉害啊:shock:
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2015-8-9 16:05:28 | 显示全部楼层
分布式爬虫~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-3-29 22:16

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表