静态爬虫实战之百度百科

  1. 目标需求
    爬取百度百科一百个词条,保存其相关的链接、标题、摘要信息
  2. 爬虫架构
    想象一下爬虫运行的过程,从计算机启动,从某个特定url开始发出网络请求,得到响应后使用bs4解析,提取出需要保存的数据和url,对新提取出的url重复上述操作,当爬取指定数量url之后,将数据存盘。最明显的我们需要两个功能,获取网络数据,解析网络数据,将这两个功能封装进类中,将数据存盘也放入类中,还需要考虑的问题是url管理,决定进程何时停止。
    从以上一般过程,总结爬虫的结构,包括url管理器、url下载器、网页解析器、数据存储器、以及管理主进程的manager。url管理器维护已经下载的url和将要下载的url的集合,保证数据不重复,url下载器根据给出的url下载网络数据,网页解析器解析给出的页面内容,提取必要信息,数据存储器将数据存盘,最后还要有管理器协调以上四者的工作
  3. 程序开发
    • url管理器代码如下
      #!/usr/bin/env python
      # coding:utf-8
      class URLManager(object):
          def __init__(self):
              self.old_urls = set()
              self.new_urls = set()
          # 获取新的url
          def get_new_url(self):
              new_url = self.new_urls.pop()
              self.old_urls.add(new_url)
              return new_url
          # 添加待处理url
          def add_new_url(self, url):
              if url is None:
                  return
              elif url not in self.old_urls and url not in self.new_urls:
                  self.new_urls.add(url)
              else:处理
                  return
          # 添加待处理url列表
          def add_new_urls(self, urls):
              if urls is None or len(urls) == 0:
                  return
              else:
                  for url in urls:
                      self.add_new_url(url)
          # 返回待处理url数量
          def new_url_size(self):
              return len(self.new_urls)
          #返回已处理url数量
          def old_url_size(self):
              return len(self.old_urls)
          # 判断是否有新的url要处理
          def has_new_urls(self):
              return self.new_url_size() != 0
      
    • 下载器
      #!/usr/bin/env python
      # coding:utf-8
      import requests
      class HtmlDownloader(object):
          def download(self, url):
              if url is None:
                  return
              else:
                  user_agent = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.170 Safari/537.36 OPR/53.0.2907.68'
                  headers = {'User-Agent':user_agent}
                  res = requests.get(url, headers=headers)
                  if res.status_code == 200:
                      res.encoding = 'utf-8'
                      return res.text
              return None
      
    • url解析器
      #!/usr/bin/env python
      # coding:utf-8
      from bs4 import BeautifulSoup
      import re
      import urlparse
      
      
      class HtmlParser(object):
      
          def _get_new_urls(self, page_url, soup):
              if page_url is None or soup is None:
                  return
              else:
                  links = []
                  tag_a = soup.find_all('a', href=re.compile(r'/item/*'))
                  # 经测试第一个和最后一个连接不可用
                  tag_a.remove(tag_a[0])
                  tag_a.remove(tag_a[len(tag_a) - 1])
                  for tag in tag_a:
                      links.append(urlparse.urljoin(page_url, tag['href']))
              return set(links)
      
          def _get_new_data(self, page_url, soup):
              if page_url is None or soup is None:
                  return
              else:
                  data = {}
                  title = soup.find_all('dd', class_='lemmaWgt-lemmaTitle-title')
                  data['title'] = title[0].h1.get_text()
                  summary = soup.find_all('div', class_='lemma-summary')
                  data['summary'] = summary[0].get_text()
                  data['url'] = page_url
              return {item:data[item] for item in data.keys()}
      
          def parser(self, page_url, page_con):
              if page_url is None or page_con is None:
                  return
              else:
                  soup = BeautifulSoup(page_con, 'html.parser')
                  data = self._get_new_data(page_url,soup)
                  new_urls = self._get_new_urls(page_url, soup)
              return new_urls, data
      
    • 数据存储
      #!/usr/bin/env python
      # encoding:utf-8
      # 编码问题
      import sys
      import codecs
      reload(sys)
      sys.setdefaultencoding('utf-8')
      
      
      class DataOutput(object):
          def __init__(self):
              self.datas = []
      
          def store_data(self, data):
              if data is None:
                  return
              else:
                  self.datas.append(data)
      
          def out_data(self):
              if len(self.datas) == 0:
                  print 'datas contains nothing...'
                  return
              else:
                  fp = codecs.open('baike.html', 'w',encoding='utf-8')
                  fp.write('<html>')
                  fp.write('<body>')
                  fp.write('<table>')
                  for data in self.datas:
                      fp.write('<tr>')
                      fp.write('<td>{}</td>'.format(data['url']))
                      fp.write('<td>{}</td>'.format(data['title']))
                      fp.write('<td>{}</td>'.format(data['summary']))
                      fp.write('</tr>')
                  fp.write('</table>')
                  fp.write('</body>')
                  fp.write('</html>')
                  fp.close()
      
    • 主程序管理
      #!/usr/bin/env python
      # coding:utf-8
      from HtmlDownloader import HtmlDownloader
      from HtmlParser import HtmlParser
      from URLManager import URLManager
      from DataOutput import DataOutput
      
      
      class SpiderMan(object):
          def __init__(self):
              self.manager = URLManager()
              self.parser = HtmlParser()
              self.out_fp = DataOutput()
              self.downloader = HtmlDownloader()
      
          def craw(self, base_url):
              if base_url is None:
                  return
              else:
                  self.manager.add_new_url(base_url)
                  while(self.manager.has_new_urls() and self.manager.old_url_size() < 100):
                      try:
                          new_url = self.manager.get_new_url()
                          page = self.downloader.download(new_url)
                          urls, data = self.parser.parser(base_url, page)
                          self.manager.add_new_urls(urls)
                          self.out_fp.store_data(data)
                          print 'already got {} links...'.format(self.manager.old_url_size())
                      except Exception,e:
                          print 'crawing failed!'
      
                  self.out_fp.out_data()
      
      if __name__ == '__main__':
          spider = SpiderMan()
          spider.craw('https://baike.baidu.com/item/Python/407313')
      
  4. 结果


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容