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的lambda匿名函数的简单介绍
Apr 25 Python
python使用urllib2模块获取gravatar头像实例
Dec 18 Python
Python中类的定义、继承及使用对象实例详解
Apr 30 Python
Python中的lstrip()方法使用简介
May 19 Python
Python中enumerate函数代码解析
Oct 31 Python
浅谈Django学习migrate和makemigrations的差别
Jan 18 Python
Python使用Django实现博客系统完整版
Sep 29 Python
使用 Python 实现文件递归遍历的三种方式
Jul 18 Python
[原创]Python入门教程1. 基本运算【四则运算、变量、math模块等】
Oct 28 Python
python tkinter组件使用详解
Sep 16 Python
Pytorch中的variable, tensor与numpy相互转化的方法
Oct 10 Python
Python3.8对可迭代解包的改进及用法详解
Oct 15 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
Content-type 的说明
2006/10/09 PHP
php2html php生成静态页函数
2008/12/08 PHP
php学习笔记之 函数声明
2011/06/09 PHP
php 缩略图实现函数代码
2011/06/23 PHP
php处理文件的小例子(解压缩,删除目录)
2013/02/03 PHP
Laravel 5.3 学习笔记之 安装
2016/08/28 PHP
谷歌浏览器 insertCell与appendChild的区别
2009/02/12 Javascript
Javascript 入门基础学习
2010/03/10 Javascript
jquery 读取页面load get post ajax 四种方式代码写法
2011/04/02 Javascript
jquery动态加载图片数据练习代码
2011/08/04 Javascript
jQuery中before()方法用法实例
2014/12/25 Javascript
Node.js实现Excel转JSON
2015/04/24 Javascript
jQuery Easyui datagrid editor为combobox时指定数据源实例
2016/12/19 Javascript
jquery Banner轮播选项卡
2016/12/26 Javascript
Bootstrap表单使用方法详解
2017/02/17 Javascript
vue动态路由配置及路由传参的方式
2018/05/23 Javascript
解决bootstrap-select 动态加载数据不显示的问题
2018/08/10 Javascript
laydate只显示时分 不显示秒的功能实现方法
2019/09/28 Javascript
python调用Moxa PCOMM Lite通过串口Ymodem协议实现发送文件
2014/08/15 Python
Python递归遍历列表及输出的实现方法
2015/05/19 Python
从头学Python之编写可执行的.py文件
2017/11/28 Python
聊聊Python中的pypy
2018/01/12 Python
基于python监控程序是否关闭
2020/01/14 Python
Windows下PyCharm配置Anaconda环境(超详细教程)
2020/07/31 Python
Python自动巡检H3C交换机实现过程解析
2020/08/14 Python
基于html5 canvas实现漫天飞雪效果实例
2014/09/10 HTML / CSS
html5的input的required使用中遇到的问题及解决方法
2018/04/24 HTML / CSS
html5移动端价格输入键盘的实现
2019/09/16 HTML / CSS
Darphin迪梵官网: 来自巴黎,植物和精油调制的护肤品牌
2016/10/11 全球购物
生日宴会答谢词
2014/01/09 职场文书
2014年大学生四年规划书范文
2014/04/03 职场文书
主要领导对照检查材料
2014/08/26 职场文书
2014党员民主评议个人思想剖析发言
2014/09/19 职场文书
对照四风自我剖析材料
2014/10/07 职场文书
新手开公司创业注意事项有哪些?
2019/07/29 职场文书
亲情作文之母爱
2019/09/25 职场文书