Python实现简易Web爬虫详解


Posted in Python onJanuary 03, 2018

简介:

网络爬虫(又被称为网页蜘蛛),网络机器人,是一种按照一定的规则,自动地抓信息的程序或者脚本。假设互联网是一张很大的蜘蛛网,每个页面之间都通过超链接这根线相互连接,那么我们的爬虫小程序就能够通过这些线不断的搜寻到新的网页。

Python作为一种代表简单主义思想的解释型、面向对象、功能强大的高级编程语言。它语法简洁并且具有动态数据类型和高层次的抽象数据结构,这使得它具有良好的跨平台特性,特别适用于爬虫等程序的实现,此外Python还提供了例如Spyder这样的爬虫框架,BeautifulSoup这样的解析框架,能够轻松的开发出各种复杂的爬虫程序。

在这篇文章中,使用Python自带的urllib和BeautifulSoup库实现了一个简单的web爬虫,用来爬取每个URL地址及其对应的标题内容。

流程:

爬虫算法从输入中读取的一个URL作为初始地址,向该地址发出一个Request请求。

请求的地址返回一个包含所有内容的,将其存入一个String变量,使用该变量实例化一个BeautifulSoup对象,该对象能够将内容并且将其解析为一个DOM树。

根据自己的需要建立正则表达式,最后借助HTML标签从中解析出需要的内容和新的URL,将新的放入队列中。

对于目前所处的URL地址与爬去的内容,在进行一定的过滤、整理后会建立索引,这是一个单词-页面的存储结构。当用户输入搜索语句后,相应的分词函数会对语句进行分解获得关键词,然后再根据每个关键词查找到相应的URL。通过这种结构,可以快速的获取这个单词所对应的地址列表。在这里使用树形结构的存储方式,Python的字典和列表类型能够较好的构建出单词词典树。

从队列中弹出目前的URL地址,在爬取队列不为空的条件下,算法不断从队列中获取到新的网页地址,并重复上述过程。

实现:

环境:

Python3.5orAnaconda3

BeautifulSoup4

可以使用下面的指令安装BeautifulSoup4,如果你是Ubuntu用户,记得在命令前面加上sudo:

pip install beautifulsoup4

程序分别实现了几个类,分别用于URL地址管理,Html内容请求、Html内容解析、索引建立以及爬虫主进程。我将整个程序按照每个Class分开解释,最后只要将他们放在一起就可以执行代码了。

UrlManager类

这个类用来管理URL地址,new_urls用来保存还未爬取的URL地址,old_urls保存了已经爬取过的地址,两个变量都使用set类型保证其中内容的唯一性。每次循环时,add_new_urls()向外提供了向new_urls变量中添加新urls的方法;add_new_url()方法,对每个url地址进行重复性检查,符合条件的才进行添加操作;get_urls()向外提供了获取新的url地址的方法;has_new_url()方法用来检查爬取队列是否为空。

import re
import urllib.request
import urllib.parse
from bs4 import BeautifulSoup


class UrlManager(object):
  def __init__(self):
    self.new_urls = set()
    self.old_urls = set()

  def add_new_url(self, url):
    if url is None:
      return
    if url not in self.new_urls and url not in self.old_urls:
      self.new_urls.add(url)

  def add_new_urls(self, urls):
    if urls is None or len(urls) == 0:
      return
    for url in urls:
      self.add_new_url(url)

  def has_new_url(self):
    return len(self.new_urls) != 0

  def get_new_url(self):
    new_url = self.new_urls.pop()
    self.old_urls.add(new_url)
    return new_url

HtmlDownloader类

这个类实现了向url地址发送Request请求,并获取其回应的方法,调用类内的download()方法就可实现。这里要注意的是页面的编码问题,这里我使用的是UTF-8来进行decode解码,有的网页可能使用的是GBK编码,要根据实际情况进行修改。

class HtmlDownloader(object):
  def download(self, url):
    if url is None:
      return None
    try:
      request = urllib.request.Request(url)
      response = urllib.request.urlopen(request)
      content = response.read().decode('utf-8').encode('utf-8')
      if content is None:
        return None
      if response.getcode() != 200:
        return None
    except urllib.request.URLError as e:
      print(e)
      return None

    return content

HtmlParser类

这个类通过实例化一个BeautifulSoup对象来进行页面的解析。它是一个使用Python编写的HTML/XML文档解析器。它通过将文档解析为DOM树的方式为用户提供需要抓取的数据,并且提供一些简单的函数用来处理导航、搜索、修改分析树等功能。

该类的关键是_get_new_urls()、_get_new_content()、get_url_title()三个方法。第一个方法用来解析出页面包含的超链接,最为重要的选择要解析的标签并为其构造合适的正则表达式。这里我为a标签定义了一个匹配正则,用来获取所有的站内链接,如下:

links = soup.find_all('a', href=re.compile(r'^(%s).*(/|html)$' % self.domain))`

后面的两个类都是通过解析Html标签来获取title的方法,最终在parse()中通过调取_get_new_content()来获得title内容。具体的标签访问方法不细谈了,读者可以自己翻阅BeautifulSoup的官方文档。

class HtmlParser(object):
  def __init__(self, domain_url):
    self.domain = domain_url
    self.res = HtmlDownloader()

  def _get_new_urls(self, page_url, soup):
    new_urls = set()
    links = soup.find_all('a', href=re.compile(r'^(%s).*(/|html)$' % self.domain))

    try:
      for link in links:
        new_url = link['href']
        new_full_url = urllib.parse.urljoin(self.domain, new_url)
        new_urls.add(new_full_url)

      new_urls = list(new_urls)
      return new_urls
    except AttributeError as e:
      print(e)
      return None

  def _get_new_content(self, page_url, soup):
    try:
      title_name = soup.title.string
      return title_name
    except AttributeError as e:
      print(e)
      return None

  def get_url_title(self):
    content = self.res.download(self.domain)

    try:
      soup = BeautifulSoup(content, 'html.parser', from_encoding='utf-8')
      title_name = soup.title.string
      return title_name
    except:
      title_name = 'None Title'
      return title_name

  def parse(self, page_url, html_cont):
    if page_url is None or html_cont is None:
      return None

    soup = BeautifulSoup(html_cont, 'html.parser', from_encoding='utf-8')
    new_data = self._get_new_content(page_url, soup)
    new_urls = self._get_new_urls(page_url, soup)

    return new_urls, new_data

BuildIndex

该类为每个URL地址与他的标题包含的关键词建立了一个索引关系并保存在一个Dict变量中,每个标题对应多个关键词,每个标题也对应多个url地址,因此每个关键词也对应了多个url地址,具体的形式如下:

index={'keyword':[url1,url2,...,urln],...}

其中,add_page_index()方法对每个标题进行了分词处理,并且调用了add_key_index()方法将keyword-url的对应关系存到索引中,这其中也进行了重复检查。主意,这个分词方法仅限于英文句子,中文的话需要用到特定的分词工具。

class BuildIndex(object):
  def add_page_index(self, index, url, content):
    words = content.split()
    for word in words:
      index = self.add_key_index(index, url, word)
    return index

  def add_key_index(self, index, url, keyword):
    if keyword in index:
      if url not in index[keyword]:
        index[keyword].append(url)
    else:
      temp = []
      index[keyword] = temp
      index[keyword].append(url)
    return index

SpiderMain

这是爬虫的主题类,它通过调用其他几个类生成的对象来实现爬虫的运行。该类实例化的时候会永久生成上面几个类的对象,当通过craw()方法获取到用户提供的url地址时,就会依次进行请求、下载、解析、建立索引的工作。最后该方法会返回index,graph两个变量,他们分别是:

每个关键词集齐对应的地址,keyword-urls索引,如下

index={'keyword':[url1,url2,...,urln],...}

每个url及其页面中包含的urls,url-suburls索引,如下

graph={'url':[url1,url2,...,urln],...}

class SpiderMain(object):
  def __init__(self, root_url):
    self.root_url = root_url
    self.urls = UrlManager()
    self.downloader = HtmlDownloader()
    self.parser = HtmlParser(self.root_url)
    self.build = BuildIndex()

  def craw(self):
    index = graph = {}
    self.urls.add_new_url(self.root_url)
    while self.urls.has_new_url():
      try:
        new_url = self.urls.get_new_url()
        html_cont = self.downloader.download(new_url)
        new_urls, new_title = self.parser.parse(new_url, html_cont)
        index = self.build.add_page_index(index, new_url, new_title)
        graph[new_url] = list(new_urls)
        self.urls.add_new_urls(new_urls)
      except Exception as e:
        print(e)
        return None

    return index, graph

最后,我们在程序中添加下面的代码,就可以成功的执行我们的爬虫了

if __name__ == '__main__':
  spider = SpiderMain('http://www.xael.org/')
  index, graph = spider.craw()
  print(index)
  print(graph)

总结

以上就是本文关于Python实现简易Web爬虫详解的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

Python 相关文章推荐
python网页请求urllib2模块简单封装代码
Feb 07 Python
在Django同1个页面中的多表单处理详解
Jan 25 Python
python3利用tcp实现文件夹远程传输
Jul 28 Python
Python使用Turtle库绘制一棵西兰花
Nov 23 Python
NumPy中的维度Axis详解
Nov 26 Python
Python开发之基于模板匹配的信用卡数字识别功能
Jan 13 Python
tensorflow tf.train.batch之数据批量读取方式
Jan 20 Python
如何基于python实现不邻接植花
May 01 Python
解决django框架model中外键不落实到数据库问题
May 20 Python
如何验证python安装成功
Jul 06 Python
浅析Python 多行匹配模式
Jul 24 Python
python如何用matplotlib创建三维图表
Jan 26 Python
Python读取MRI并显示为灰度图像实例代码
Jan 03 #Python
使用 Python 实现微信公众号粉丝迁移流程
Jan 03 #Python
EM算法的python实现的方法步骤
Jan 02 #Python
Python+树莓派+YOLO打造一款人工智能照相机
Jan 02 #Python
matplotlib绘制动画代码示例
Jan 02 #Python
Python+matplotlib+numpy实现在不同平面的二维条形图
Jan 02 #Python
Python 实现淘宝秒杀的示例代码
Jan 02 #Python
You might like
DC宇宙的第一个英雄,堪称动漫史鼻祖,如今成为美国文化的象征
2020/04/09 欧美动漫
php数组相加 array(“a”)+array(“b”)结果还是array(“a”)
2012/09/19 PHP
PH P5.2至5.5、5.6的新增功能详解
2014/07/14 PHP
[原创]PHPCMS遭遇会员投稿审核无效的解决方法
2017/01/11 PHP
Javascript里使用Dom操作Xml
2007/01/22 Javascript
jquery1.4 教程二 ajax方法的改进
2010/02/25 Javascript
javascript中的注释使用与注意事项小结
2011/09/20 Javascript
JavaScript插件化开发教程 (二)
2015/01/27 Javascript
node.js操作mongodb学习小结
2015/04/25 Javascript
jQuery编程中的一些核心方法简介
2015/08/14 Javascript
jQuery拖动布局其结果保存到数据库
2015/10/09 Javascript
JavaScript实现的多种鼠标拖放效果
2015/11/03 Javascript
全面解析Bootstrap中nav、collapse的使用方法
2016/05/22 Javascript
jQuery四种选择器使用及示例
2016/06/05 Javascript
利用fecha进行JS日期处理
2016/11/21 Javascript
微信小程序 支付功能实现PHP实例详解
2017/05/12 Javascript
基于layer.js实现收货地址弹框选择然后返回相应的地址信息
2017/05/26 Javascript
bootstrap table支持高度百分比的实例代码
2018/02/28 Javascript
Vue项目安装插件并保存
2019/01/28 Javascript
在Vue中实现随hash改变响应菜单高亮
2020/03/09 Javascript
详解JS预解析原理
2020/06/16 Javascript
[03:34]2014DOTA2西雅图国际邀请赛 淘汰赛7月15日TOPPLAY
2014/07/15 DOTA
Python写的英文字符大小写转换代码示例
2015/03/06 Python
python2.7和NLTK安装详细教程
2018/09/19 Python
Linux上使用Python统计每天的键盘输入次数
2019/04/17 Python
python导入坐标点的具体操作
2019/05/10 Python
Python实现时间序列可视化的方法
2019/08/06 Python
Python自动化导出zabbix数据并发邮件脚本
2019/08/16 Python
django model object序列化实例
2020/03/13 Python
Django数据库迁移常见使用方法
2020/11/12 Python
python中time.ctime()实例用法
2021/02/03 Python
Amara德国:家居饰品、设计师品牌和豪华礼品
2019/05/20 全球购物
面试后的英文感谢信
2014/02/01 职场文书
公司股权转让协议书
2014/04/12 职场文书
十周年庆典策划方案
2014/06/03 职场文书
创业开店,这样方式更合理
2019/08/26 职场文书