热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

2018.2最新Scrapy+elasticSearch+Django打造搜索引擎直至部署上线(三)

最终项目上线演示地址:http:search.mtianyan.cn第三节:本节我们先得了解点你做爬虫做搜索引擎的居家必备知识基础。Github地址:https:gi

最终项目上线演示地址: http://search.mtianyan.cn

  • 第三节:本节我们先得了解点你做爬虫做搜索引擎的居家必备知识基础。

Github地址: https://github.com/mtianyan/ArticleSpider (欢迎先点个star后上车)

爬虫基础知识
  1. 正则表达式
  2. 深度优先和广度优先遍历算法
  3. url去重的常见策略

技术选型

scrapy vs requests+beautifulsoup

  • requests和beautifulsoup都只是库,scrapy是框架。
  • scrapy 可以加入requests和beautifulsoup
  • scrapy基于twisted(异步IO框架),性能好
  • 方便扩展,有很多内置功能
  • 内置的cssxpath selector(lxml:c实现的框架)很方便,速度快

beautifulsoup最大的缺点就是慢。

pip install scrapy

可能遇到的报错信息: Failed building wheel for Twisted

http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted 本地安装

网页分类

  1. 静态网页
  2. 动态页面
  3. webservice(restful api): 前台通过ajax与后台进行交互

爬虫能做什么?

  1. 搜索引擎:百度 垂直领域搜索引擎(只爬某领域)
  2. 推荐引擎:今日头条
  3. 机器学习的数据样本
  4. 数据分析(金融数据分析) 舆情分析

正则表达式

为什么必须会正则表达式?

应用场景: 1天前中提取出1

特殊字符:

可以判断是否匹配模式,字符串中提取指定字符

  • ^:代表以该字符为开头。如^b就是b为开头。
  • .: 代表任意一个字符。如^b.就是b开头后面一个字母任意
  • *: 代表前一个字符可以出现任意次(0个也可以)。如^b.*就是b开头后面可有任意数个任意字母
  • $: 代表前一个字符为结尾符合。 如:.*3$就是以3为结尾的任意字符串
  • ?: 在限定次数词后加一个"?",则表示匹配尽可能少的字符: 非贪婪匹配。
    • 一般情况从右往左匹配正则,非贪婪遇到符合最小条件就返回。
    • ?可以实现从左往右,非贪婪指遇到第一个就结束。
      .*?(b.*?b).*就是首先开始的b和找到的第一个b之间的字符串,不向后贪婪。

Example Code

# encoding: utf-8
__author__ = 'mtianyan'
__date__ = '2018/1/17 0017 16:20'import reline1 = "mtianyan123"
line2 = "moooommtianyan123"# 定义正则表达式的字符串模式
# ^m代表以m开头;.代表任意字符;* 代表前面字符可以重复任意遍
# 3$代表以3为结尾
regex_str1 = "^m.*3$"# 两个参数: 定义的字符串,以及我们要检验的字符串
if re.match(regex_str1, line1):print("yes")# 默认为贪婪模式: 反向由字符串模式从右向左匹配
# m前面无所谓啥,m和后一个m中间无所谓啥。后面也无所谓有啥。目标:moooom
# 括号括起来我们需要的字符串.也就是只提取这一部分。
regex_str2 = ".*(m.*m).*"
match_obj = re.match(regex_str2, line2)
if match_obj:# 只取第一个括号东西。print (match_obj.group(1))
# mm# 使用?使他从左边开始但是右边还是贪婪模式匹配。从右匹配到右起第一个
regex_str3 = ".*?(m.*m).*"
match_obj = re.match(regex_str3, line2)
if match_obj:# 只取第一个括号东西。print (match_obj.group(1))
# moooomm# 使用?使他从左边开始但是右边还是贪婪模式匹配。
# 在第二个m前也加问号。让它从左起开始。找这样的条件的b,不要继续向后贪婪
regex_str4 = ".*?(m.*?m).*"
match_obj = re.match(regex_str4, line2)
if match_obj:# 只取第一个括号东西。print (match_obj.group(1))
# moooom

  • 两个问号都不加: 默认从右开始找匹配模式的字符,找到即返回。
  • 加前面一个问号: 从左开始,但是会一直贪婪到最长的符合字符。因为后面m还是从右开始的。

  • 后面加一个问号: 第二个字符从左开始找。

  • 两个问号都加:从左开始找字符m 和 m

  • +: 代表前面的字符至少出现一次: 如b.+b指两个b之间至少一个字符

+*{2}都属于限定词限定前面出现多少次

  • {2} {2,5} {2, }:
    • 代表前面的字符出现2次。
    • 代表前面的字符出现2到5次.
    • 代表前面字符出现2次及两次以上
  • |:代表的意思 b|c,在模式字符串从左到右, 看先匹配到那个。先找b
  • (): 代表子字符串。如((a|b)123)代表group(1)为外层括号里的a123|b123;group(2)为内层括号值 a b
  • []: 代表[]内的字符满足任意一个都可以。
    • [abcdefg]123代表以abcdefg中一个字母开头的,后面是123的都可以。
  • [0-9] [a-z]
    • 代表可以为0-9范围内任意一个字符
    • 代表a-z范围任意一个字符
  • [^1] ^为取反,只要不等于1就可以

注: 进入[]的字符都不再具有特殊含义[.*]就指匹配到.或者*

  • \s \S
    • \s代表匹配到一个空格。
    • \S代表匹配到一个非空格
  • \w \W
    • 匹配任意字符 等价: [a-zA-Z0-9_]
    • 非单词字符
  • [\u4E00-\u9FA5]: 匹配中文汉字

"study in 浙江大学" 匹配regex_str = .*?[\u4E00-\u9FA5]+大学

如果不加问号。则从右开始,只会匹配到江大学

  • \d: 匹配数字

例子: "xxx出生于2001年" 匹配.*?(\d+)年

不加?会只匹配到1.因为它从右往左找第一个符合条件的。

  • 加上问号,从左往右,找到符合条件的。

import reline = "XXX出生于2001年"
# line = "XXX出生于2001/6/1"
# line = "XXX出生于2001-6-1"
# line = "XXX出生于2001-06-01"
# line = "XXX出生于2001-06"regex_str = ".*出生于(\d{4}[年/-]\d{1,2}([月/-]\d{1,2}日|[月/-]\d{1,2}|[月/-]$|$))"match_obj = re.match(regex_str, line)
if match_obj:print (match_obj.group(1))

([月/-]\d{1,2}日|[月/-]\d{1,2}|[月/-]$|$)满足或的关系

深度优先和广度优先

  1. 网站的树结构
  2. 深度优先算法和实现
  3. 广度优先算法和实现

网站url树结构:

分层设计
子域名:

  1. jobbole.com
    1.1 blog.bogbole.com
    1.2 python.bogbole.com
    1.1.1 python.bogbole.com/123
mark

上图为网站的实际url结构图,可以看到其中的环路链接:

从首页到某个具体页面节点。
但是下面的链接节点又会有链接指向首页,这样会让爬虫自己在里面转圈圈。

去重, 建立已爬取url列表

  1. 深度优先
  2. 广度优先

跳过已爬取的链接, 对于二叉树的遍历问题

mark

深度优先(递归实现):

顺着一条路,走到最深处。然后回头。

输出: A->B->D->E->I->C->F->G-H (scrapy默认使用)

广度优先(队列实现):

分层遍历:遍历完儿子辈。然后遍历孙子辈

输出: A->B->C->D->E->F->G->H->I

Python实现深度优先过程code:

def depth_tree(tree_node):if tree_node is not None:print (tree_node._data)if tree_node._left is not None:return depth_tree(tree_node.left)if tree_node._right is not None:return depth_tree(tree_node,_right)

递归太深没有跳出,会造成内存溢出。

Python实现广度优先过程code:

def level_queue(root):#利用队列实现树的广度优先遍历if root is None:returnmy_queue = []node = rootmy_queue.append(node)while my_queue:node = my_queue.pop(0)print (node.elem)if node.lchild is not None:my_queue.append(node.lchild)if node.rchild is not None:my_queue.append(node.rchild)

爬虫去重策略

  1. 将访问过的url保存到数据库中,获取url时查询一下是否爬过了。
  2. 将url保存到set中。只需要O(1)的代价就可以查询到url
  3. url经过md5等方法哈希后保存到set中,将url压缩到固定长度而且不重复
  4. bitmap方法,将访问过的url通过hash函数映射到某一位.
  5. bloomfilter方法对bitmap进行改进,多重hash函数降低冲突可能性。

方法2保守估计:100000000*2byte*50个字符/1024/1024/1024 = 9G,占用内存过大。

scrapy去重使用的是第三种方法:

后面分布式scrapy-redis会讲解bloomfilter方法。hash函数会引起冲突,冲突解决。

Python字符串编码问题解决:

  1. 计算机只能处理数字,文本转换为数字才能处理,计算机中8个bit作为一个字节,
    所以一个字节能表示的最大数字就是255。
  2. 计算机是美国人发明的,所以一个字节就可以标识所有单个字符
    ,所以ASCII(一个字节)编码就成为美国人的标准编码
  3. 但是ASCII处理中文明显不够,中文不止255个汉字,所以中国制定了GB2312编码
    ,用两个字节表示一个汉字。GB2312将ASCII也包含进去了。同理,日文,韩文,越来越多的国家为了解决这个问题就都发展了一套编码,标准越来越多,如果出现多种语言混合显示就一定会出现乱码
  4. 于是unicode出现了,通过16个bit, 32个bit.它将所有语言包含进去了。
  5. 看一下ASCII和unicode编码:
    • 字母A用ASCII编码十进制是65,二进制 0100 0001
    • 汉字"中" 已近超出ASCII编码的范围,用unicode编码是20013二进制是01001110 00101101
    • A用unicode编码只需要前面补0二进制是 00000000 0100 0001
  6. 乱码问题解决了,但是如果内容全是英文,unicode编码比ASCII编码需要多一倍的存储空间,传输也会变慢。
  7. 所以此时出现了可变长的编码"utf-8" ,把英文:1字节,汉字3字节,特别生僻的变成4-6字节,如果传输大量的英文,utf8作用就很明显。

Unicode编码虽然占用空间但是因为占用空间大小等额,在内存中处理会简单一些。

读取文件,进行操作时:转换为unicode编码进行处理
保存文件时,转换为utf-8编码。以便于传输

mark

读文件进行decode(解码)操作转码为Unicode,但是decode必须指明原始编码方式。
Unicode编码 encode(编码) 操作转码为其他我们指定编码格式。

读文件的库都会自动将文件转换为Unicode编码,但需要我们指明编码。

Python3中将所有字符转换为Unicode。

python2 默认编码格式为ASCII,Python3 默认编码为 utf-8

#python3
import sys
sys.getdefaultencoding()
s.encoding('utf-8')

#python2
import sys
sys.getdefaultencoding()
s = "我和你"
su = u"我和你"
~~s.encode("utf-8")#会报错~~
s.decode("gb2312").encode("utf-8")
su.encode("utf-8")

mark

内存中是通过Unicode进行编码的, s 是一个非Unicode的编码。windows下是gb2312编码。Linux下是utf-8的编码。

encode方法之前,必须确保前面的变量是Unicode。

mark

说明windows中s确实是gb2312保存的,而且decode方法并不会直接改变字符串本身。而是返回一个编码过后的字符串。

mark

说明Linux中s是以utf-8保存的。encode编码之前必须走中间路径: 转换为Unicode

mark
mark

可以看到windows和Linux的默认编码格式都是ascii、

Python2文件头添加utf-8编码说明。可以使得读文件的解释器指定decode的参数。
Python3已经不用加了,因为它把默认的s取消掉了。直接将字符串全部当unicode处理。
可以直接编码。

mark

可以看到windows,Linux中Python3的默认编码为utf-8.




推荐阅读
  • 本文详细介绍了如何在本地环境中安装配置Frida及其服务器组件,以及如何通过Frida进行基本的应用程序动态分析,包括获取应用版本和加载的类信息。 ... [详细]
  • CSS模块化命名 ... [详细]
  • 本文介绍了两个重要的Node.js库——cache-content-type和mime-types,它们在处理HTTP响应头时非常有用。cache-content-type是基于mime-types构建的,并且实现了缓存机制以提高性能。 ... [详细]
  • 作者提供代码在https:github.comthunlpOpenNRE网址主要技术:文章主要是通过一对实体和对应的多个包含实体对的句子实例作为训练数据集ÿ ... [详细]
  • 本文档提供了在Windows 10操作系统中安装Python 3及Scrapy框架的完整指南,包括必要的依赖库如wheel、lxml、pyOpenSSL、Twisted和pywin32的安装方法。 ... [详细]
  • 本文提供了一个详尽的前端开发资源列表,涵盖了从基础入门到高级应用的各个方面,包括HTML5、CSS3、JavaScript框架及库、移动开发、API接口、工具与插件等。 ... [详细]
  • 使用IntelliJ IDEA高效开发与运行Shell脚本
    本文介绍了如何利用IntelliJ IDEA中的BashSupport插件来增强Shell脚本的开发体验,包括插件的安装、配置以及脚本的运行方法。 ... [详细]
  • 正则表达式入门指南
    本文基于《正则表达式必知必会》(作者:Ben Forta,译者:杨涛),介绍了正则表达式的基本概念及其应用,包括搜索与替换功能,以及元字符的分类与使用。 ... [详细]
  • 本文深入探讨了微信小程序直播中点赞动画的实现方法,特别是如何利用三阶贝塞尔曲线使点赞图标沿预设路径移动,以及相关的数学计算与代码实现。 ... [详细]
  • 2023年1月28日网络安全热点
    涵盖最新的网络安全动态,包括OpenSSH和WordPress的安全更新、VirtualBox提权漏洞、以及谷歌推出的新证书验证机制等内容。 ... [详细]
  • Docker基础入门与环境配置指南
    本文介绍了Docker——一款用Go语言编写的开源应用程序容器引擎。通过Docker,用户能够将应用及其依赖打包进容器内,实现高效、轻量级的虚拟化。容器之间采用沙箱机制,确保彼此隔离且资源消耗低。 ... [详细]
  • 本文介绍了如何利用 Apache NiFi 的灵活性和扩展性,通过自定义组件来解决标准组件无法满足的特定业务需求。文章不仅涵盖了自定义处理器的基本步骤,还讨论了调试自定义组件时可能遇到的问题及解决方案。 ... [详细]
  • 页面预渲染适用于主要包含静态内容的页面。对于依赖大量API调用的动态页面,建议采用SSR(服务器端渲染),如Nuxt等框架。更多优化策略可参见:https://github.com/HaoChuan9421/vue-cli3-optimization ... [详细]
  • 本文旨在探讨Swift中的Closure与Objective-C中的Block之间的区别与联系,通过定义、使用方式以及外部变量捕获等方面的比较,帮助开发者更好地理解这两种机制的特点及应用场景。 ... [详细]
  • 如何寻找程序员的兼职机会
    随着远程工作的兴起,越来越多的程序员开始寻找灵活的兼职工作机会。本文将介绍几个适合程序员、设计师、翻译等专业人士的在线平台,帮助他们找到合适的兼职项目。 ... [详细]
author-avatar
豆伊一
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有