1. 基本的爬虫工作原理
  2. 基本的http抓取原理,scrapy
  3. Bloom Filter:
  4. 分布式爬虫概念
  5. rq和Scrapy的结合
  6. 后续处理,网页析取

假如是一只蜘蛛,爬到第一个页面,然后把页面全部抄下来,用脑子存下所看过的页面地址,每次想爬一个新链接都要去查一下这个页面是否去过,去过就不去了。(地址判重)

流程

发送请求–获得页面–解析页面–下载内容–存储内容。

准备内容:

  1. list,dict:用来序列化你爬的东西

  2. 切片:用来对爬取的内容进行分割,生成

  3. 条件判断(if等):用来解决爬虫过程中哪些要哪些不要的问题

  4. 循环和迭代(for while ):用来循环,重复爬虫动作

  5. 文件读写操作:用来读取参数、保存爬下来的内容等

url管理器:管理将要爬取的url和已经爬取的url,防止重复抓取和循环抓取。

网页下载器

网页解析器

具体流程

将url管理器中待爬取的url送给网页下载器,网页下载器将其存储下来存为一个字符串,传送给网页解析器进行解析,每个网页都有指向其他网页的url也可以将其解析之后送给url管理器。

形成循环

爬虫架构动态工作流程

  1. 调度器去问url管理器有没有待爬取的url,返回是或否给调度器,如果是的话,调度器从url管理器中取出一个待爬取的url,url管理器将这个url返回给调度器。
  2. 现在调度器得到了这个url,调度器将url传送给下载器下载内容,下载好之后将url内容返回给调度器。
  3. 调度器将url的内容传送给解析器进行网页解析。解析之后返回有价值的数据和新的url列表。
  4. 一方面调度器将价值数据传送给应用进行数据收集,另一方面将新的url列表补充到url管理器。
  5. 如果url管理器还有新的待爬url,就将其循环。
  6. 等到url管理器没有待爬的url,将其价值数据发到应用上。

网页下载器urllib下载的三种方法:
1. python 3.x中urllib库和urilib2库合并成了urllib库。
其中urllib2.urlopen()变成了urllib.request.urlopen()
urllib2.Request()变成了urllib.request.Request()

import urllib #加载urllib模块

response = urllib.request.urlopen("http://www.baidu.com")#直接请求

print(response.getcode())#获取网页的返回码

cont  = response.read()#读取内容

print(cont)
  1. 添加data,http header
    向服务器提交需要用户输入的数据,将url,data,http header三个参数传送给urllib.request.Request()类,生成一个request对象,然后依然用urllib.request.urlopen打开
    ,以request作为参数。
import urllib.request

url = 'https://www.zhihu.com/'

req = urllib.request.Request(url)

req.add_data('a','1')#添加数据

req.add_header('User-Agent','Mozilla')#伪装成浏览器

f = urllib.request.urlopen(req)

print(f.read().decode('utf-8'))

3添加特殊情景的处理器

有些网页需要用户登录才能访问,有些网页需要代理才能访问,还有一些网页是用https加密的,

++Cookie,有时也用其复数形式 Cookies,指某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密)。++

import http.cookiejar, urllib

cj = http.cookiejar.CookieJar()#创建cookie容器

opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj))#创建一个opener

urllib.request.install_opener(opener)#给urllib安装opener

url = 'https://www.zhihu.com/'

response = urllib.request.urlopen(url)

print(response.read())

网页解析器:从网页中提取有价值数据的工具

结构化解析,通过一个树来进行遍历。

创建beautifulsoup对象,搜索节点find_all,find,访问节点,名称,属性,文字。

find_all(name,attrs,string)#节点的名称,节点的属性,节点的文本

from bs4 import BeautifulSoup

soup = BeautifulSoup(html_doc,'html.parser')

links = soup.find_all('a')

for link in links:
    print(link.name,link['href'],link.get_text())
link_node = soup.find('a',href='http://example.com/lacie')

print(link_node.name,link_node['href'],link_node.get_text())

正则匹配

p_node = soup.find('p',class_="title")

print(p_node.name,p_node.get_text())
实例爬虫
  • 确定目标

抓取百度百科python词条以及相关词条页面的标题和简介

  • 分析目标
    1. 抓取目标页面的url格式,限定抓取页面的范围
    2. 分析抓取数据的格式,本实例就是抓取标题和简介
    3. 分析页面的编码
  • 编写代码
  • 执行代码

入口页

http://baike.baidu.com/item/Python

新建一个python package

# main
from baike_spider import html_downloader
from baike_spider import html_outputer
from baike_spider import html_parser
from baike_spider import url_manager

class SpiderMain(object):
    def __init__(self):
        self.urls = url_manager.UrlManager()
        self.downloader = html_downloader.HtmlDownloader()
        self.parser = html_parser.HtmlParser()
        self.outputer = html_outputer.HtmlOutputer()

    def craw(self, root_url):
        count = 1
        self.urls.add_new_url(root_url)
        while self.urls.has_new_url():#待爬取url不为空
            try:
                new_url = self.urls.get_new_url()#取出待爬取URL集合中的一个url
                print('craw %d : %s'%(count, new_url))
                html_cont = self.downloader.download(new_url)#将这个url指向的网页下载下来
                new_urls, new_data  =  self.parser.parse(new_url, html_cont)
                self.urls.add_new_urls(new_urls)
                self.outputer.collect_data(new_data)

                if(count == 1000):
                    break

                count = count+1
            except:
                print('craw failed')

        self.outputer.output_html()



if __name__=="__main__":
    root_url = "http://baike.baidu.com/item/%E4%BA%92%E8%81%94%E7%BD%91/199186"
    obj_spider = SpiderMain()
    obj_spider.craw(root_url)

其余的是module

#url管理器
class UrlManager(object):

    def __init__(self):
        self.new_urls = set()#待爬取的url集合
        self.old_urls = set()#已爬取的url集合

    def add_new_url(self, url):#添加新的url
        if url is None:
            return
        if url not in self.new_urls and url not in self.old_urls:#如果这个url不在待爬取的url中也不在已爬取的url中
                self.new_urls.add(url)#那么就将其添加到待爬取的url集合中

    def add_new_urls(self, urls):#添加url集合
        if urls is None or len(urls) == 0:
            return
        for url in urls:
            self.add_new_url(url)#进行单个的添加

    def has_new_url(self):#判断待爬取的url集合是否为空
        return len(self.new_urls) != 0#不为空的话return1 为空的话return0

    def get_new_url(self):#取出待爬取集合中的url
        new_url = self.new_urls.pop()
        self.old_urls.add(new_url)#将这个url添加到已爬取的url集合中
        return new_url
#网页下载器
import urllib.request
class HtmlDownloader(object):
    def download(self, url):
        if url is None:
            return None

        response = urllib.request.urlopen(url)#将url下载

        if response.getcode() != 200:#网页访问失败
            return None

        return response.read()#返回网页的字符串
#网页解析器

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


class HtmlParser(object):

    def _get_new_urls(self, page_url, soup):
        new_urls = set()
        links = soup.find_all('a',href = re.compile(r"/view/\d+\.htm"))
        for link in links:
            new_url = link['href']
            new_full_url = urljoin(page_url,new_url)
            new_urls.add(new_full_url)
        return new_urls

    def _get_new_data(self, page_url, soup):

        res_data = {}

        res_data['url'] = page_url

        #<dd class="lemmaWgt-lemmaTitle-title">
        #<h1>Python</h1>

        title_node = soup.find('dd',class_="lemmaWgt-lemmaTitle-title").find("h1")
        res_data['title'] = title_node.get_text()
        #<div class="lemma-summary" label-module="lemmaSummary">
        summary_node = soup.find('div',class_ = "lemma-summary")
        res_data['summary'] = summary_node.get_text()

        return res_data

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

       soup = BeautifulSoup(html_cont,'html.parser')
       new_urls = self._get_new_urls(page_url,soup)
       new_data = self._get_new_data(page_url,soup)
       return new_urls, new_data
#输出
class HtmlOutputer(object):
    def __init__(self):
        self.datas = []
    def collect_data(self, data):
        if data is None:
            return
        self.datas.append(data)

    def output_html(self):
        fout = open('output.html','w',encoding ='utf-8')

        fout.write("<html>")
        fout.write("<body>")
        fout.write("<table>")

        for data in self.datas:
            fout.write("<tr>")
            fout.write("<td>%s</td>" % data['url']+'\n')
            fout.write("<td>%s</td>" % data['title']+'\n')
            fout.write("<td>%s</td>" % data['summary']+'\n')
        fout.write("/<table>")
        fout.write("/<body>")
        fout.write("/<html>")