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 13 Python
两个命令把 Vim 打造成 Python IDE的方法
Mar 20 Python
Python用list或dict字段模式读取文件的方法
Jan 10 Python
vscode 远程调试python的方法
Dec 01 Python
在Django下创建项目以及设置settings.py教程
Dec 03 Python
关于Pytorch MaxUnpool2d中size操作方式
Jan 03 Python
tensorflow2.0保存和恢复模型3种方法
Feb 03 Python
Python+appium框架原生代码实现App自动化测试详解
Mar 06 Python
基于python爬取链家二手房信息代码示例
Oct 21 Python
浅析pandas随机排列与随机抽样
Jan 22 Python
python批量更改目录名/文件名的方法
Apr 18 Python
Python 实现Mac 屏幕截图详解
Oct 05 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 杂谈《重构-改善既有代码的设计》之一 重新组织你的函数
2012/04/09 PHP
ThinkPHP行为扩展Behavior应用实例详解
2014/07/22 PHP
php生成PDF格式文件并且加密
2015/06/22 PHP
PHP编程开发怎么提高编程效率 提高PHP编程技术
2015/11/09 PHP
PHP htmlspecialchars() 函数实例代码及用法大全
2018/09/18 PHP
PHP自动载入类文件函数__autoload的使用方法
2019/03/25 PHP
再谈ie和firefox下的document.all属性
2009/10/21 Javascript
js创建子窗口并且回传值示例代码
2013/07/02 Javascript
javascript验证只能输入数字和一个小数点示例
2013/10/21 Javascript
jquery 设置元素相对于另一个元素的top值(实例代码)
2013/11/06 Javascript
判断文件是否正在被使用的JS代码
2013/12/21 Javascript
jquery实现文字单行横移或翻转(上下、左右跳转)
2017/01/08 Javascript
Node.js中多进程模块Cluster的介绍与使用
2017/05/27 Javascript
JavaScript条件判断_动力节点Java学院整理
2017/06/26 Javascript
AngularJS中控制器函数的定义与使用方法示例
2017/10/10 Javascript
vue单页应用加百度统计代码(亲测有效)
2018/01/31 Javascript
Vue的土著指令和自定义指令实例详解
2018/02/04 Javascript
JavaScript事件冒泡机制原理实例解析
2020/01/14 Javascript
24个解决实际问题的ES6代码片段(小结)
2020/02/02 Javascript
Python SQLAlchemy入门教程(基本用法)
2019/11/11 Python
python的等深分箱实例
2019/11/22 Python
Python hashlib常见摘要算法详解
2020/01/13 Python
Python闭包与装饰器原理及实例解析
2020/04/30 Python
CSS3制作日历实现代码
2012/01/21 HTML / CSS
Html5移动端适配IphoneX等机型的方法
2019/06/25 HTML / CSS
基于Html5 canvas实现裁剪图片和马赛克功能及又拍云上传图片 功能
2019/07/09 HTML / CSS
Myprotein瑞士官方网站:运动营养和健身网上商店
2019/09/25 全球购物
俄罗斯韩国化妆品网上商店:Cosmasi.ru
2019/10/31 全球购物
出纳员岗位职责
2014/03/13 职场文书
高中语文课后反思
2014/04/27 职场文书
教师民族团结演讲稿
2014/08/27 职场文书
我为党旗添光彩演讲稿
2014/09/13 职场文书
转让协议书范本
2014/09/13 职场文书
2016年安康杯竞赛活动总结
2016/04/05 职场文书
Nginx使用X-Accel-Redirect实现静态文件下载的统计、鉴权、防盗链、限速等
2021/04/04 Servers
分享MySQL常用 内核 Debug 几种常见方法
2022/03/17 MySQL