python面向对象多线程爬虫爬取搜狐页面的实例代码


Posted in Python onMay 31, 2018

首先我们需要几个包:requests, lxml, bs4, pymongo, redis

1. 创建爬虫对象,具有的几个行为:抓取页面,解析页面,抽取页面,储存页面

class Spider(object):
 def __init__(self):
  # 状态(是否工作)
  self.status = SpiderStatus.IDLE
 # 抓取页面
 def fetch(self, current_url):
  pass
 # 解析页面
 def parse(self, html_page):
  pass
 # 抽取页面
 def extract(self, html_page):
  pass
 # 储存页面
 def store(self, data_dict):
  pass

2. 设置爬虫属性,没有在爬取和在爬取中,我们用一个类封装, @unique使里面元素独一无二,Enum和unique需要从 enum里面导入:

@unique
class SpiderStatus(Enum):
 IDLE = 0
 WORKING = 1

3. 重写多线程的类:

class SpiderThread(Thread):
 def __init__(self, spider, tasks):
  super().__init__(daemon=True)
  self.spider = spider
  self.tasks = tasks
 def run(self):
  while True:
   pass

4. 现在爬虫的基本结构已经做完了,在main函数创建tasks, Queue需要从queue里面导入:

def main():
 # list没有锁,所以使用Queue比较安全, task_queue=[]也可以使用,Queue 是先进先出结构, 即 FIFO
 task_queue = Queue()
 # 往队列放种子url, 即搜狐手机端的url
 task_queue.put('http://m.sohu,com/')
 # 指定起多少个线程
 spider_threads = [SpiderThread(Spider(), task_queue) for _ in range(10)]
 for spider_thread in spider_threads:
  spider_thread.start()
 # 控制主线程不能停下,如果队列里有东西,任务不能停, 或者spider处于工作状态,也不能停
 while task_queue.empty() or is_any_alive(spider_threads):
  pass
 print('Over')

4-1. 而 is_any_threads则是判断线程里是否有spider还活着,所以我们再写一个函数来封装一下:

def is_any_alive(spider_threads):
 return any([spider_thread.spider.status == SpiderStatus.WORKING
    for spider_thread in spider_threads])

5. 所有的结构已经全部写完,接下来就是可以填补爬虫部分的代码,在SpiderThread(Thread)里面,开始写爬虫运行 run 的方法,即线程起来后,要做的事情:

def run(self):
  while True:
   # 获取url
   current_url = self.tasks_queue.get()
   visited_urls.add(current_url)
   # 把爬虫的status改成working
   self.spider.status = SpiderStatus.WORKING
   # 获取页面
   html_page = self.spider.fetch(current_url)
   # 判断页面是否为空
   if html_page not in [None, '']:
    # 去解析这个页面, 拿到列表
    url_links = self.spider.parse(html_page)
    # 把解析完的结构加到 self.tasks_queue里面来
    # 没有一次性添加到队列的方法 用循环添加算求了
    for url_link in url_links:
     self.tasks_queue.put(url_link)
   # 完成任务,状态变回IDLE
   self.spider.status = SpiderStatus.IDLE

6.  现在可以开始写 Spider()这个类里面的四个方法,首先写fetch()抓取页面里面的:  

@Retry()
 def fetch(self, current_url, *, charsets=('utf-8', ), user_agent=None, proxies=None):
  thread_name = current_thread().name
  print(f'[{thread_name}]: {current_url}')
  headers = {'user-agent': user_agent} if user_agent else {}
  resp = requests.get(current_url,
       headers=headers, proxies=proxies)
  # 判断状态码,只要200的页面
  return decode_page(resp.content, charsets) \
   if resp.status_code == 200 else None

6-1. decode_page是我们在类的外面封装一个解码的函数:

def decode_page(page_bytes, charsets=('utf-8',)):
 page_html = None
 for charset in charsets:
  try:
   page_html = page_bytes.decode(charset)
   break
  except UnicodeDecodeError:
   pass
   # logging.error('Decode:', error)
 return page_html

6-2. @retry是装饰器,用于重试, 因为需要传参,在这里我们用一个类来包装, 所以最后改成@Retry():

# retry的类,重试次数3次,时间5秒(这样写在装饰器就不用传参数类), 异常
class Retry(object):
 def __init__(self, *, retry_times=3, wait_secs=5, errors=(Exception, )):
  self.retry_times = retry_times
  self.wait_secs = wait_secs
  self.errors = errors
 # call 方法传参
 def __call__(self, fn):
  def wrapper(*args, **kwargs):
   for _ in range(self.retry_times):
    try:
     return fn(*args, **kwargs)
    except self.errors as e:
     # 打日志
     logging.error(e)
     # 最小避让 self.wait_secs 再发起请求(最小避让时间)
     sleep((random() + 1) * self.wait_secs)
   return None
  return wrapper()

7. 接下来写解析页面的方法,即 parse():

# 解析页面
 def parse(self, html_page, *, domain='m.sohu.com'):
  soup = BeautifulSoup(html_page, 'lxml')
  url_links = []
  # 找body的有 href 属性的 a 标签
  for a_tag in soup.body.select('a[href]'):
   # 拿到这个属性
   parser = urlparse(a_tag.attrs['href'])
   netloc = parser.netloc or domain
   scheme = parser.scheme or 'http'
   netloc = parser.netloc or 'm.sohu.com'
   # 只爬取 domain 底下的
   if scheme != 'javascript' and netloc == domain:
    path = parser.path
    query = '?' + parser.query if parser.query else ''
    full_url = f'{scheme}://{netloc}{path}{query}'
    if full_url not in visited_urls:
     url_links.append(full_url)
  return url_links

7-1. 我们需要在SpiderThread()的 run方法里面,在

current_url = self.tasks_queue.get()

下面添加

visited_urls.add(current_url)

在类外面再添加一个

visited_urls = set()去重

8. 现在已经能开始抓取到相应的网址。

python面向对象多线程爬虫爬取搜狐页面的实例代码 

总结

以上所述是小编给大家介绍的python面向对象多线程爬虫爬取搜狐页面的实例代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Python 相关文章推荐
Python装饰器原理与简单用法实例分析
Apr 29 Python
python实现自动登录
Sep 17 Python
selenium+python设置爬虫代理IP的方法
Nov 29 Python
通过shell+python实现企业微信预警
Mar 07 Python
python实现Virginia无密钥解密
Mar 20 Python
使用Python检测文章抄袭及去重算法原理解析
Jun 14 Python
python用for循环求和的方法总结
Jul 08 Python
python字符串常用方法及文件简单读写的操作方法
Mar 04 Python
Python实现子类调用父类的初始化实例
Mar 12 Python
python简单的三元一次方程求解实例
Apr 02 Python
python怎么调用自己的函数
Jul 01 Python
Python Matplotlib绘制动画的代码详解
May 30 Python
Python中if elif else及缩进的使用简述
May 31 #Python
python基于物品协同过滤算法实现代码
May 31 #Python
python写入并获取剪切板内容的实例
May 31 #Python
python3实现基于用户的协同过滤
May 31 #Python
python控制windows剪贴板,向剪贴板中写入图片的实例
May 31 #Python
python用户评论标签匹配的解决方法
May 31 #Python
python批量查询、汉字去重处理CSV文件
May 31 #Python
You might like
一个不易被发现的PHP后门代码解析
2014/07/05 PHP
php获得网站访问统计信息类Compete API用法实例
2015/04/02 PHP
PHP使用Nginx实现反向代理
2017/09/20 PHP
PHP实现redis限制单ip、单用户的访问次数功能示例
2018/06/16 PHP
Jquery拖拽并简单保存的实现代码
2010/11/28 Javascript
FF(火狐)浏览器无法执行window.close()解决方案
2014/11/13 Javascript
提升PHP安全:8个必须修改的PHP默认配置
2014/11/17 Javascript
JavaScript数据类型检测代码分享
2015/01/26 Javascript
JavaScript中的Repaint和Reflow用法详解
2015/07/27 Javascript
程序员必知35个jQuery 代码片段
2015/11/05 Javascript
总结JavaScript设计模式编程中的享元模式使用
2016/05/21 Javascript
JS 滚动事件window.onscroll与position:fixed写兼容IE6的回到顶部组件
2016/10/10 Javascript
Bootstrap的modal拖动效果
2016/12/25 Javascript
微信小程序开发之animation循环动画实现的让云朵飘效果
2017/07/14 Javascript
Angular4如何自定义首屏的加载动画详解
2017/07/26 Javascript
微信{"errcode":48001,"errmsg":"api unauthorized, hints: [ req_id: 1QoCla0699ns81 ]"}
2018/10/12 Javascript
JS中创建自定义类型的常用模式总结【工厂模式,构造函数模式,原型模式,动态原型模式等】
2019/01/19 Javascript
浅谈vue异步数据影响页面渲染
2019/10/29 Javascript
vue 取出v-for循环中的index值实例
2019/11/09 Javascript
JS+CSS实现炫酷光感效果
2020/09/05 Javascript
[01:01:01]完美世界DOTA2联赛循环赛 GXR vs FTD BO2第一场 10.29
2020/10/29 DOTA
Python pass 语句使用示例
2014/03/11 Python
Python实现同时兼容老版和新版Socket协议的一个简单WebSocket服务器
2014/06/04 Python
探究数组排序提升Python程序的循环的运行效率的原因
2015/04/01 Python
Python进行数据提取的方法总结
2016/08/22 Python
keras自定义损失函数并且模型加载的写法介绍
2020/06/15 Python
python 利用matplotlib在3D空间中绘制平面的案例
2021/02/06 Python
css3 图片圆形显示 如何CSS将正方形图片显示为圆形图片布局
2014/10/10 HTML / CSS
英国网上超市:Ocado
2020/03/05 全球购物
Araks官网:纽约内衣品牌
2020/10/15 全球购物
简述网络文件系统NFS,并说明其作用
2016/10/19 面试题
运动会通讯稿100字
2014/01/31 职场文书
三方协议书
2015/01/27 职场文书
毕业酒会致辞
2015/07/29 职场文书
导游词之太行山青龙峡
2020/01/14 职场文书
Python编程根据字典列表相同键的值进行合并
2021/10/05 Python