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 正则式 概述及常用字符
May 07 Python
Python中的Classes和Metaclasses详解
Apr 02 Python
Python加载带有注释的Json文件实例
May 23 Python
Python高级特性切片(Slice)操作详解
Sep 27 Python
python pip源配置,pip配置文件存放位置的方法
Jul 12 Python
python函数的作用域及关键字详解
Aug 20 Python
Python实现生成密码字典的方法示例
Sep 02 Python
Pytorch十九种损失函数的使用详解
Apr 29 Python
python re的findall和finditer的区别详解
Nov 15 Python
Python3中的tuple函数知识点讲解
Jan 03 Python
Python 快速验证代理IP是否有效的方法实现
Jul 15 Python
Python利用Turtle绘制哆啦A梦和小猪佩奇
Apr 04 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
win2003服务器使用WPS的COM组件的一些问题解决方法
2012/01/11 PHP
LotusPhp笔记之:基于ObjectUtil组件的使用分析
2013/05/06 PHP
深入PHP变量存储的详解
2013/06/13 PHP
php通过ajax实现双击table修改内容
2014/04/28 PHP
php中strstr、strrchr、substr、stristr四个函数的区别总结
2014/09/22 PHP
Windows2003下php5.4安装配置教程(Apache2.4)
2016/06/30 PHP
疯掉了,尽然有js写的操作系统
2007/04/23 Javascript
Prototype Array对象 学习
2009/07/19 Javascript
jQuery表单验证插件formValidator(改进版)
2012/02/03 Javascript
详解Node.js如何开发命令行工具
2016/08/14 Javascript
jQuery Mobile和HTML5开发App推广注册页
2016/11/07 Javascript
微信小程序 详解Page中data数据操作和函数调用
2017/01/12 Javascript
HTML5+Canvas调用手机拍照功能实现图片上传(下)
2017/04/21 Javascript
JS获取一个表单字段中多条数据并转化为json格式
2017/10/17 Javascript
vue中实现图片和文件上传的示例代码
2018/03/16 Javascript
vue实现未登录跳转到登录页面的方法
2018/07/17 Javascript
Vue 开发音乐播放器之歌手页右侧快速入口功能
2018/08/08 Javascript
Vue2 添加数据可视化支持的方法步骤
2019/01/02 Javascript
js实现GIF图片的分解和合成
2019/10/24 Javascript
js仿360开机效果
2019/12/26 Javascript
js实现鼠标滑动到某个div禁止滚动
2020/09/17 Javascript
Vue实现购物小球抛物线的方法实例
2020/11/22 Vue.js
如何在VUE中使用vue-awesome-swiper
2021/01/04 Vue.js
[03:58]兄弟们,回来开黑了!DOTA2昔日战友招募宣传视频
2016/07/17 DOTA
Python实现的多线程端口扫描工具分享
2015/01/21 Python
pandas 将索引值相加的方法
2018/11/15 Python
Python学习笔记之Zip和Enumerate用法实例分析
2019/08/14 Python
PyTorch的SoftMax交叉熵损失和梯度用法
2020/01/15 Python
详解Python 函数参数的拆解
2020/09/02 Python
Kathmandu新西兰官网:新西兰户外运动品牌
2019/07/27 全球购物
机关党员三严三实心得体会
2014/10/13 职场文书
委托书格式要求
2015/01/28 职场文书
2015建军节87周年演讲稿
2015/03/19 职场文书
学风建设主题班会
2015/08/17 职场文书
SpringBoot系列之MongoDB Aggregations用法详解
2022/02/12 MongoDB
Python Numpy库的超详细教程
2022/04/06 Python