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中实现从目录中过滤出指定文件类型的文件
Feb 02 Python
Python基于Matplotlib库简单绘制折线图的方法示例
Aug 14 Python
python使用正则表达式替换匹配成功的组并输出替换的次数
Nov 22 Python
python 3.6 +pyMysql 操作mysql数据库(实例讲解)
Dec 20 Python
Python 2.7中文显示与处理方法
Jul 16 Python
python ---lambda匿名函数介绍
Mar 13 Python
Django页面数据的缓存与使用的具体方法
Apr 23 Python
python3实现绘制二维点图
Dec 04 Python
python装饰器原理与用法深入详解
Dec 19 Python
django使用channels实现通信的示例
Oct 19 Python
Opencv常见图像格式Data Type及代码实例
Nov 02 Python
numpy实现RNN原理实现
Mar 02 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
php简单对象与数组的转换函数代码(php多层数组和对象的转换)
2011/05/18 PHP
关于WordPress的SEO优化相关的一些PHP页面脚本技巧
2015/12/10 PHP
浅谈Laravel中的一个后期静态绑定
2017/08/11 PHP
javascript下阻止表单重复提交、防刷新、防后退
2007/08/17 Javascript
Javascript 入门基础学习
2010/03/10 Javascript
利用jQuery插件扩展识别浏览器内核与外壳的类型和版本的实现代码
2011/10/22 Javascript
jQuery中判断一个元素是否为另一个元素的子元素(或者其本身)
2012/03/21 Javascript
ie中js创建checkbox默认选中问题探讨
2013/10/21 Javascript
js时间比较示例分享(日期比较)
2014/03/05 Javascript
JS是按值传递还是按引用传递
2015/01/30 Javascript
javascript简单实现类似QQ头像弹出效果的方法
2015/08/03 Javascript
js文本框输入内容智能提示效果
2015/12/02 Javascript
Angular.js实现多个checkbox只能选择一个的方法示例
2017/02/24 Javascript
微信小程序 image组件遇到的问题
2019/05/28 Javascript
layui table设置某一行的字体颜色方法
2019/09/05 Javascript
Vue+abp微信扫码登录的实现代码示例
2020/01/06 Javascript
VUE子组件向父组件传值详解(含传多值及添加额外参数场景)
2020/09/01 Javascript
JS相册图片抖动放大展示效果的示例代码
2021/01/29 Javascript
[02:22]《新闻直播间》2017年08月14日
2017/08/15 DOTA
[05:24]TI9采访——教练
2019/08/24 DOTA
python调用cmd复制文件代码分享
2013/12/27 Python
Python中read()、readline()和readlines()三者间的区别和用法
2017/07/30 Python
基于scrapy的redis安装和配置方法
2018/06/13 Python
python实发邮件实例详解
2019/11/11 Python
Python如何脚本过滤文件中的注释
2020/05/27 Python
读取nii或nii.gz文件中的信息即输出图像操作
2020/07/01 Python
CSS3制作翻转效果_动力节点Java学院整理
2017/07/11 HTML / CSS
香港中原电器网上商店:Chung Yuen
2019/06/26 全球购物
小学生评语集锦
2014/04/18 职场文书
给校长的一封检讨书
2014/09/20 职场文书
避暑山庄导游词
2015/02/04 职场文书
元宵节寄语大全
2015/02/27 职场文书
如何写新闻稿
2015/07/18 职场文书
关于考试抄袭的检讨书
2019/11/02 职场文书
如何利用Python实现n*n螺旋矩阵
2022/01/18 Python
Go本地测试解耦任务拆解及沟通详解Go本地测试的思路沟通的重要性总结
2022/06/21 Golang