整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

jQuery 管理和筛选选择器

jQuery 管理和筛选选择器

取元素的个数

$("img").size()

获取jQuery选择器中元素的个数:

<script language="javascript" src="jquery.min.js"></script>
<script language="javascript">
document.onclick=function(){
    var i=$("div").size()+1; //获取div块的数目(此时还没有添加div块)
    $(document.body).append($("<div>"+i+"</div>")); //添加一个div块
    $("span").html(i); //修改显示的总数
}
</script>

HTML代码:

页面中一共有<span>0</span>个div块。点击鼠标添加div:

注意:$(document.body)可以简化为$("body")

jQuery1.8废弃了size() 用length代替

提取元素

$("img[title]")[1] 获取所有设置了title属性的img标记中的第2个元素

以下代码与上面完全等效

$("img[title]").get(1); //从0开始计数

另外,get()方法在不设置任何参数时,可以将元素转化为一个元素对象的数组。

jQuery提取选择器中的元素:

<script language="javascript" src="jquery.min.js"></script>
<script language="javascript">
function disp(divs){
    for(var i=0;i<divs.length;i++)
        $(document.body).append($("<div style='background:"+divs[i].style.background+";'>"+divs[i].innerHTML+"</div>"));
}
$(function(){
    var aDiv=$("div").get(); //转化为div对象数组
    disp(aDiv.reverse()); //反序,传给处理函数
});
</script>
<div style="background:#FFFFFF">1</div>
<div style="background:#CCCCCC">2</div>
<div style="background:#999999">3</div>
<div style="background:#666666">4</div>
<div style="background:#333333">5</div>
<div style="background:#000000">6</div>

注意:get([index])取得其中一个匹配的元素。index表示取得第几个匹配的元素。

这能够让你选择一个实际的DOM元素并且对他直接操作, 而不是通过jQuery函数。$("div").get(0)与$("div")[0]等价。

反过来index(element)方法可以查找元素element所处的位置

例如:var iNum=$("li").index($("li[title=isaac]")[0]);

用index()方法获取元素的序号

<script language="javascript" src="jquery.min.js"></script>
<script language="javascript">
$(function(){
    //click()添加点击事件
    $("div").click(function(){
    //将块本身用this关键字传入,从而获取自身的序号
    var index=$("div").index(this);
    $("span").html(index.toString());
    });
});
</script>
<div>0</div><div>1</div><div>2</div><div>3</div><div>4</div><div>5</div>
点击的div块序号为:<span></span>
查找元素的索引值
HTML 代码:
<ul>
<li id="foo">foo</li>
<li id="bar">bar</li>
<li id="baz">baz</li>
</ul>

jQuery 代码:

$('li').index(document.getElementById('bar')); //1, 传递一个DOM对象, 返回这个对象在原先集合中的索引位置

$('li').index($('#bar')); //1, 传递一个jQuery对象

$('li').index($('li:gt(0)')); //1, 传递一组jQuery对象, 返回这个对象中第一个元素在原先集合中的索引位置

$('#bar').index('li'); //1, 传递一个标记, 返回#bar在所有li中的索引位置

$('#bar').index(); //1, 不传递参数, 返回这个元素在同辈中的索引位置

index()注意的问题:

<ul>
<li>娱乐</li>
<img src="img/on.png"/>
<li>政治</li>
<img src="img/on.png"/>
<li>体育</li>
<img src="img/on.png"/>
<li>娱乐</li>
<li>娱乐</li>
</ul>
$("li").click(function(){
    //index内部是吧钙元素的所有兄弟进行排序
    //但是我们布局中经常会有一些多余的元素用来装饰 但是我不想让这些元素参与排序
    //jq内部也考虑到了这一点 他们改了
    console.log($(this).index()); //排序包括img和li(0 2 4 7 8)
    console.log($(this).index("li"));//我仅仅针对li进行排序
    //他沟通过(选择器)里面的参数进行过滤
})
children() 查询子元素
<p>Hello</p><div><span>Hello Again</span></div><p>And Again</p>

jQuery 代码:

$("div").children()

结果:

[ <span>Hello Again</span> ]


parent() 查询父元素

<div><p>Hello</p><p>Hello</p></div>

jQuery 代码:

$("p").parent()

结果:

[ <div><p>Hello</p><p>Hello</p></div>]

siblings() 查询兄弟节点元素

<p>Hello</p><div><span>Hello Again</span></div><p>And Again</p>

jQuery 代码:

$("div").siblings()

结果:

[ <p>Hello</p>, <p>And Again</p> ]

next() 查询紧邻的下一个元素

找到每个段落的后面紧邻的同辈元素。

HTML 代码:

<p>Hello</p><p>Hello Again</p><div><span>And Again</span></div>

jQuery 代码:

$("p").next()

结果:

[ <p>Hello Again</p>, <div><span>And Again</span></div> ]

prev() 查询紧邻的上一个元素

找到每个段落紧邻的前一个同辈元素。

HTML 代码:

<p>Hello</p><div><span>Hello Again</span></div><p>And Again</p>

jQuery 代码:

$("p").prev()

结果:

[ <div><span>Hello Again</span></div> ]

添加、删除、过滤元素

添加元素

$("img[alt]").add("img[title]");

等同于:

$("img[alt],img[title]");
$("img[alt]").add("img[title]").addClass("myClass");

等同于

$("img[alt],img[title]").addClass("myClass");

删除元素

$("li[title]").not("[title*=isaac]");
$("div").not(".green, #blueone").addClass("myClass");

去掉风格为.green #blueone 的类 并添加.myClass的类

注意:not()方法所接受的参数都不能包含特定的元素,只能是通用的表达式,例如下面是典型的错误代码:

$("li[title]").not("img[title*=isaac]")

正确的写法为:

$("li[title]").not("[title*=isaac]")

过滤元素

$("li").filter("[title*=isaac]"); 

此种方法与not()一样, 但是功能更强大。

以上代码等同于

$("li[title*=issac]")

filter语句还于jQuery语句的链接

$(function(){
		$("div").addClass("myClass1").filter("[class*=middle]").addClass("myClass2");
});

注意:filter("[class=middle]")语句得不到理想的效果 需要添加* ^ $(经过测试jQuery支持后匹配有问题,包括最新版本jquery2.0.1)

filter()另外一种类型的参数是函数,它可以让用户自定义筛选函数,该函数要求返回一个布尔型值,参数为index为元素所处的序号(从零还是计数)

如:

$(function(){
$("div").addClass("myClass1").filter(function(index){ //index为元素所处的序号(从零还是计数)
    return index==1 || $(this).attr("id")=="fourth";
}).addClass("myClass2");
});

以上代码为整个<div>列表增加样式风格"myClass1",然后进行筛选,再为筛选出的元素单独增加样式风格"myClass2"。如果不采用jQuery链,将非常麻烦。

查询过滤新元素集合

$("p").find("span");

以上代码表示所有<p>标记的元素中搜索<span>标记,获得一个新的元素集合,等同于

$("span",$("p")); //注意顺序不能调换
如:
$(function(){
		$("p").find("span").addClass("myClass");
});

另外,还可以通过is()方法来检测是否包括指定的元素

var bHasuImage=$("div").is("img"); //返回值为Boolean;

采用jQuery链

在jQuery链中,后面的操作都是以前面的操作结果为对象的。如果希望操作对象为上一步的对象,则可以使用end()方法。

$(function(){
		$("p").find("span").addClass("myClass1").end().addClass("myClass2");
});

利用end()方法将操作对象往回设置为$("p");

另外andSelf()方法控制jQuery链 将前面两个对象进行组合后共同处理。

$(function(){
		$("p").find("span").addClass("myClass1").andSelf().addClass("myClass2");
});

andSelf()方法将<p>和<span>组合在一起,添加样式风格,这个风格对<p>和<span>均有效;

小结:

删除元素.not()方法和过滤元素.filter()方法,所接受的参数都不能包含特定的元素,只能是通用表达式

$("li[title]").not("img[title*=isaac]")
$("li[title]").filter("img[title*=isaac]")

应当改写为:

$("li[title]").not("[title*=isaac]")
$("li[title]").filter("[title*=isaac]")

同时不能使用直接的等于匹配(=),只能使用前匹配(^=)、任意匹配(*=),

而后匹配($=)(有些书写的是&=),无论后匹配是$=还是&=,jQuery程序多次测试有问题;

综合实例:

保留子元素中不含有ol的元素。

HTML 代码:

<p><ol><li>Hello</li></ol></p><p>How are you?</p>

jQuery 代码:

$("p").filter(function(index) {
    return $("ol", this).length==0;
});

结果:

[ <p>How are you?</p> ]

理解:$("ol", this)等同于$(this).find("ol"),this关键字代表遍历后的p标记

以上语句可改成:(添加了让DOM立即执行的函数)

、筛选请求

  1. 使用过滤,需勾选User Filter才会生效,运行当前的过滤规则,需点击Run Filterset now

  1. Hosts:主机过滤之局域网过滤

  • No Zone Filter:默认选项,不设置区域过滤器
  • show only Intranet Hosts:只显示内网的Hosts,即过滤掉外网的Hosts。此时,只显示URL请求目标服务器在内网的请求
  • show only internet Hosts:与上相对应,只显示外网的Hosts
  1. Hosts:主机过滤之广域网过滤

  • No Host Filter:默认选项,不设置过滤主机IP条件
  • Hide the following Hosts:隐藏输入到下面文本框中的Hosts
  • Show only the following Hosts:仅显示输入到下面文本框中的Hosts
  • Flag the following Hosts:标识下边输入的主机名的会话(加粗)
  1. Client process:客户端进程过滤

  • Show only traffic from:指定只显示哪个Windows进程中的请求,右侧会列出当前所有的 Windows 进程。
  • Show only Internet Explorer traffic:只显示IE发出的请求。
  • Hide trafficfrom service host:隐藏来自service
  1. Request header:请求头过滤

  • Show only if URL contains:仅显示URL中包含指定字符串的请求,字符串可以是URL中的一部分,多个字符串用空格分开,也可以使用EXACT:前缀来限定大小写,或者使用REGEX:前缀正则表达式或完整的URL。
  • Hide if URL contains:仅隐藏URL中包含指定字符串的请求,字符串可以是URL中的一部分,多个用空格分开,可以是正则或完整的URL。
  • Flag requests with headers:标记带有特定header的请求,如果在websession列表中存在该请求,会加粗显示。
  • Delete request headers:删除请求中指定的Header字段
  • Set request Header:在URL请求中添加头字段,与上一条对应。前面输入字段,后面输入值。
  1. Breakpionts:设置断点

  • Break request on Post:对所有Post请求设置断点
  • Break request on GET with query string:对所有带参数的 GET 请求设置断点
  • Break on XMLHttpRequest:对所有Ajax请求设置断点
  • Break response on Content-type:对响应报文中header字段中,Content-Type属性匹配成功的请求设置断点
  1. Response Status Code:响应状态码过滤

  • Hide success(2xx):隐藏响应状态码为2xx的URL请求
  • Hide non-2xx:隐藏响应状态码不是2xx的URL请求
  • Hide Authentication demands(401,407):隐藏认证(响应状态码为:401 407)的URL请求
  • Hide redirects(300,301,302,303,307):隐藏重定向(响应状态码为:300,301,302,303,07)的URL请求
  • Hide Not Modifield(304):隐藏不是缓存的状态码(304)的URL请求
  1. Response Type and Size :响应类型和报文大小过滤

  • 下拉类别选择显示类型
Show all Content-Type:显示所有类型Content-Type的请求,Content-Type即为响应结果header信息中Content-Type字段
Show only IMAGE/*:仅显示响应类型为图片的请求
Show only HTML:仅显示响应类型为HTML的请求
Show only TEXT/CSS:仅显示响应类型为text/css的请求
Show only SCRIPTS:仅显示响应类型为Scripts的请求
Show only XML:仅显示响应类型为XML的请求
Show only JSON:仅显示响应类型为Json的请求
Hide IMAGE/*:隐藏所有响应类型为图片的请求
  • Hide smaller than:隐藏小于指定大小响应结果的请求。
  • Hide larger than:隐藏大于指定大小响应结果的请求。
  • Time HeatMap:时间热图。
  • Block script files:阻止返回正常JS文件,如果响应是脚本文件,那么响应 404。
  • Block image files:阻止返回正常图片文件,如果响应是图片文件,那么响应 404。
  • Block SWF files:阻止返回正常SWF文件,如果响应是SWF 文件,那么响应 404。
  • Block CSS files:阻止返回正常CSS文件,如果响应是 CSS 文件,那么响应 404。
  1. Response Header:响应头过滤

  • Flag responses that set cookies:会话列表的响应中有cookies信息的加上标识(斜体)。
  • Flag responses with headers:会话列表里标记响应中带有特定header的请求(即加粗)。
  • Delete response headers:会话列表里删除响应信息中,有指定的Header字段的会话。
  • Set response header:在响应中添加header字段,第一个输入框是header 的key,第二个输入框是对应value

二、修改请求参数之临时修改参数

  1. 临时修改参数,可通过urlreplace SeekStr ReplaceWithStr命令,做参数替换
  2. 打开Fiddler,再打开百度,搜索csdn,在Fiddler中查看参数,wd是查询参数

  1. Fiddler命令行输入urlreplace wd=csdn wd=js回车,回到浏览器点击百度一下,会发现虽然搜索的是csdn,但是查出来的是js相关的信息,这种方法关闭Fiddler之后就会失效

三、断点修改请求参数

  1. 在请球之前拦截请求,以刚才的csdn为例

  1. 点击百度一下,会发现已经进入Fiddler断点

  1. 点击请求,点击Inspectors>WebForms,修改参数中的wd为js,点击Run to Complation,会发现虽然搜索的是csdn,但是查出来的是js相关的信息

  1. 这种方法修改参数,如果遇到请求参数中有时间戳的很容易造成请求失败,而且断点会断住所有请求,如果想断某个请求,最好还是做个请求筛选

四、通过Composer修改参数

  1. 先通过浏览器百度csdn,Fiddler中查看Inspectors>WebView会看到搜索出来的是csdn,如果是乱码点击Decode解码一下

  1. 鼠标左键把请求拖到Composer,在Url中找到wd,修改为js,点击执行,查看Inspectors>WebView会看到搜索出来的是js

  1. 这种方法修改参数,如果遇到请求参数中有时间戳的很容易造成请求失败,而且不会在浏览器上体现出来,相当于postman创建了一个请求,好处就是会有历史记录下次使用可以直接在历史记录里找

五、修改请求参数之永久修改参数,FiddlerScript修改参数

  1. 找到FiddlerScript编辑脚本,也可以在工具栏Rules > Customize Rules中修改脚本

  1. 找到static function OnBeforeRequest,添加oSession.uriContains("web=csdn")代码,点击save script保存脚本

  1. 打开浏览器搜索csdn,会发现虽然搜索的是csdn,但是查出来的是js相关的信息,这种方法关闭浏览器不会失效、也不用打断点,缺点是需要熟悉.Net,不过需要什么代码,度娘应该会给你答案

六、修改请求脚本代码

  1. 拦截post请求,并修改JSON格式参数
if (oSession.HTTPMethodIs("POST") && oSession.uriContains("example.com")) {
    // 获取请求的 Body 数据
    var requestBody=oSession.GetRequestBodyAsString();

    // 解析 JSON
    var json=Fiddler.WebFormats.JSON.JsonDecode(requestBody);


    // 修改 JSON 参数
    json.JSONObject["Body"]["kwy"]="value";
    // 删除 JSON 参数
    json.JSONObject.Remove('key');
    json.JSONObject["Body"].Remove("key");
    
    // 将修改后的 JSON 转换回字符串
    var modifiedRequestBody=Fiddler.WebFormats.JSON.JsonEncode(json.JSONObject);
    // 更新请求的 Body 数据
    oSession.utilSetRequestBody(modifiedRequestBody);
}
  1. 拦截post请求,并修改普通参数,非JSON格式
if (oSession.HTTPMethodIs("POST") && oSession.uriContains("example.com")) {
    // 获取请求的 Body 数据
    var requestBody=oSession.GetRequestBodyAsString();

    // 修改参数
    requestBody=requestBody.replace("param1=value1", "param1=newValue");

    // 更新请求的 Body 数据
    oSession.utilSetRequestBody(requestBody);
}
  1. 拦截get请求,并修改url参数,如果post请求url中带有参数,可以把GET换成POST
if (oSession.HTTPMethodIs("GET") && oSession.uriContains("example.com")) {
    // 获取请求的 URL
    var url=oSession.fullUrl;

    // 修改参数
    url=url.replace("param1=value1", "param1=newvalue");

    // 更新请求的 URL
    oSession.fullUrl=url;
}
  1. 拦截get请求,并删除url参数,如果post请求url中带有参数,可以把GET换成POST
import System.Web;

if (oSession.HTTPMethodIs("GET") & & oSession.uriContains("example.com")) {
    // 获取请求的 URL
    var url=oSession.fullUrl;
    
    // 解析 URL 中的参数
    var uri=new System.Uri(url);
    var queryString=System.Web.HttpUtility.ParseQueryString(uri.Query);
    
    // 删除指定的参数
    queryString.Remove("dt");
    
    // 构建新的 URL
    var newUrl=uri.GetLeftPart(System.UriPartial.Path) + "?" + queryString.ToString();
    
    // 更新请求的 URL
    oSession.fullUrl=newUrl;
}
  1. 拦截get请求,并修改请求头,如果需要修改POST请求头,可以把GET换成POST
if (oSession.HTTPMethodIs("GET") && oSession.uriContains("example.com")) {
    // 获取请求的头部对象
    var headers=oSession.oRequest.headers;

    // 修改头部的值
    headers["key"]="value";

    // 删除指定的头部
    headers.Remove("key");
}
  1. 拦截get请求,并修改cookie,如果需要修改POST请求cookie,可以把GET换成POST
if (oSession.HTTPMethodIs("GET") && oSession.uriContains("example.com")) {
    // 获取请求的 Cookie 值
    var cookieValue=oSession.oRequest["Cookie"];
    
    // 添加cookie
    cookieValue+=";key=value";
    
    // 修改cookie
    cookieValue=cookieValue.Replace("key=value", "key=newValue");
    
    // 删除cookie
    cookieValue=cookieValue.Replace("key=value", "");

    oSession.oRequest["Cookie"]=cookieValue
}
  1. 正则替换字符串,以修改cookie为例
if (oSession.HTTPMethodIs("GET") && oSession.uriContains("example.com")) {
    // 获取请求的 Cookie 值
    var cookieValue=oSession.oRequest["Cookie"];
    
    var pattern="key=([^&]*)";
    var regex=new System.Text.RegularExpressions.Regex(pattern);
    var cookieValue=regex.Replace(cookieValue, "key=newValue");

    oSession.oRequest["Cookie"]=cookieValue
}
  1. 输出cookie到控制台,可通过log查看
FiddlerApplication.Log.LogString("cookie: " + cookieValue);

虫处理之结构化数据操作

目录

  1. 正则表达式提取数据

  2. 正则表达式案例操作

  3. Xpath提取数据

  4. Xpath案例操作

  5. BeautifulSoup4提取数据

  6. BeautifulSoup4案例操作

章节内容

1. 关于数据

爬虫程序,主要是运行在网络中进行数据采集的一种计算机程序,正常的一个爬虫采集数据的过程大致如下:

  • 访问目标服务器

  • 采集数据,获取访问url的数据

  • 根据需要筛选数据

  • 处理数据,存储到文件或者数据库,等待下一步进行数据分析或者数据展示

由于存在着不同的服务器和软件应用,所以爬虫获取到的数据就会出现各种不同的表现形式,但是总体来说还是有规律的,有规律就可以被掌握的


首先,关于爬虫处理的数据,一般分为两种数据

  • 非结构化数据:数据的内容整体没有固定的格式和语法规范

  • 结构化数据:数据的内容有固定的语法规范,按照一定的结构进行组织管理

这两种数据都分别表示什么样的数据呢,分别应该通过什么样的方式进行处理呢,这是爬虫在采集完数据之后,针对数据进行筛选必须要进行的操作


接下来,了解两种不同的数据的表现形式

  • 非结构化数据

    无格式字符串数据:用户名、邮箱、账号、电话号码、地址、电影名称、评分、评论、商品名称等等

  • 结构化数据

    带有一定的格式的数据:HTML网页文档、XML网页文档、JSON等等


第三,对于不同的数据,进行有效数据筛选时,应该分别怎么进行操作呢

  • 非结构化数据:由于没有任何固定的格式,只能根据字符出现的规律进行动态匹配的方式来完成数据的提取:正则表达式

  • 结构化数据:由于数据本身存在一定的规律性,可以通过针对这些规律的分析工具进行数据的提取:正则表达式、Xpath、BeautifulSoup4、select、css等等

2. 正则表达式

正则表达式是一门单独的技术,在实际操作过程中由于它优雅的字符匹配特性,各种编程语言都陆续支持正则表达式的操作方式,Python中通过内建模块re进行正则表达式的处理,大致按照如下三个步骤进行数据的操作:

  • 确定源数据:获取整体数据

  • 按照目标数据定义正则表达式匹配规则

  • 从整体数据中匹配符合要求的数据

正则表达式的处理,最核心的是先掌握正则表达式的语法和匹配规则,根据实际操作的不同需要,正则表达式定义了不同的数据匹配方式

匹配规则规则描述
\将下一个字符标记为一个特殊字符、或一个原义字符、或一个向后引用、或一个八进制转义符。例如,“n”匹配字符“n”。“\n”匹配一个换行符。串行“\”匹配“\”而“(”则匹配“(”。
^匹配输入字符串的开始位置。如果设置了RegExp对象的Multiline属性,^也匹配“\n”或“\r”之后的位置。
$匹配输入字符串的结束位置。如果设置了RegExp对象的Multiline属性,$也匹配“\n”或“\r”之前的位置。
*匹配前面的子表达式零次或多次。例如,zo能匹配“z”以及“zoo”。等价于{0,}。
+匹配前面的子表达式一次或多次。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等价于{1,}。
?匹配前面的子表达式零次或一次。例如,“do(es)?”可以匹配“does”或“does”中的“do”。?等价于{0,1}。
{n}n是一个非负整数。匹配确定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的两个o。
{n,}n是一个非负整数。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。“o{1,}”等价于“o+”。“o{0,}”则等价于“o*”。
{n,m}m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”将匹配“fooooood”中的前三个o。“o{0,1}”等价于“o?”。请注意在逗号和两个数之间不能有空格。
?当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串“oooo”,“o+?”将匹配单个“o”,而“o+”将匹配所有“o”。
.匹配除“\n”之外的任何单个字符。要匹配包括“\n”在内的任何字符,请使用像“(.|\n)”的模式。
(pattern)匹配pattern并获取这一匹配。所获取的匹配可以从产生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中则使用>匹配pattern并获取这一匹配。所获取的匹配可以从产生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中则使用$0…$9属性。要匹配圆括号字符,请使用“\(”或“\)”。<…属性。要匹配圆括号字符,请使用“\(”或“\)”。
(?:pattern)匹配pattern但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用或字符“(|)”来组合一个模式的各个部分是很有用。例如“industr(?:y|ies)”就是一个比“industry|industries”更简略的表达式。
(?=pattern)正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,“Windows(?=95|98|NT|2000)”能匹配“Windows2000”中的“Windows”,但不能匹配“Windows3.1”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
(?!pattern)正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如“Windows(?!95|98|NT|2000)”能匹配“Windows3.1”中的“Windows”,但不能匹配“Windows2000”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始
(?<=pattern)反向肯定预查,与正向肯定预查类拟,只是方向相反。例如,“(?<=95|98|NT|2000)Windows”能匹配“2000Windows”中的“Windows”,但不能匹配“3.1Windows”中的“Windows”。
(?<!pattern)反向否定预查,与正向否定预查类拟,只是方向相反。例如“(?<!95|98|NT|2000)Windows”能匹配“3.1Windows”中的“Windows”,但不能匹配“2000Windows”中的“Windows”。
x|y匹配x或y。例如,“z|food”能匹配“z”或“food”。“(z|f)ood”则匹配“zood”或“food”。
[xyz]字符集合。匹配所包含的任意一个字符。例如,“[abc]”可以匹配“plain”中的“a”。
[^xyz]负值字符集合。匹配未包含的任意字符。例如,“[^abc]”可以匹配“plain”中的“p”。
[a-z]字符范围。匹配指定范围内的任意字符。例如,“[a-z]”可以匹配“a”到“z”范围内的任意小写字母字符。
[^a-z]负值字符范围。匹配任何不在指定范围内的任意字符。例如,“[^a-z]”可以匹配任何不在“a”到“z”范围内的任意字符。
\b匹配一个单词边界,也就是指单词和空格间的位置。例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”。
\B匹配非单词边界。“er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”。
\cx匹配由x指明的控制字符。例如,\cM匹配一个Control-M或回车符。x的值必须为A-Z或a-z之一。否则,将c视为一个原义的“c”字符。
\d匹配一个数字字符。等价于[0-9]。
\D匹配一个非数字字符。等价于[^0-9]。
\f匹配一个换页符。等价于\x0c和\cL。
\n匹配一个换行符。等价于\x0a和\cJ。
\r匹配一个回车符。等价于\x0d和\cM。
\s匹配任何空白字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]。
\S匹配任何非空白字符。等价于[^ \f\n\r\t\v]。
\t匹配一个制表符。等价于\x09和\cI。
\v匹配一个垂直制表符。等价于\x0b和\cK。
\w匹配包括下划线的任何单词字符。等价于“[A-Za-z0-9_]”。
\W匹配任何非单词字符。等价于“[^A-Za-z0-9_]”。
\xn匹配n,其中n为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,“\x41”匹配“A”。“\x041”则等价于“\x04&1”。正则表达式中可以使用ASCII编码。.
\num匹配num,其中num是一个正整数。对所获取的匹配的引用。例如,“(.)”匹配两个连续的相同字符。
\n标识一个八进制转义值或一个向后引用。如果\n之前至少n个获取的子表达式,则n为向后引用。否则,如果n为八进制数字(0-7),则n为一个八进制转义值。
\nm标识一个八进制转义值或一个向后引用。如果\nm之前至少有nm个获得子表达式,则nm为向后引用。如果\nm之前至少有n个获取,则n为一个后跟文字m的向后引用。如果前面的条件都不满足,若n和m均为八进制数字(0-7),则\nm将匹配八进制转义值nm。
\nml如果n为八进制数字(0-3),且m和l均为八进制数字(0-7),则匹配八进制转义值nml。
\un匹配n,其中n是一个用四个十六进制数字表示的Unicode字符。例如,\u00A9匹配版权符号(?)。

3. python操作正则表达式

python内置了re模块,可以很方便快捷的操作正则表达式语法完成字符串的查询匹配操作行为,需要注意的是通过re操作正则表达式的两种表现形式

第一种方式主要是通过compile()函数根据给定的正则表达式编译生成正则匹配对象,通过正则匹配对象完成字符串的查询匹配操作过程

import re# 定义正则表达式,通过compile()函数编译pattern=re.compile('正则表达式')# 核心操作函数# 1.起始位置匹配一次:仅从指定的起始位置进行匹配(默认开头位置)# 匹配成功返回匹配到的字符串,表示目标字符串是该字符串开头的# 匹配失败返回Nonevalue=pattern.match(string[, start[ , end]])# 2.全文匹配一次:从指定的起始位置开始匹配(默认开头位置)# 陆续对字符串中的所有字符进行匹配# 匹配成功返回匹配到的字符串,表示目标字符串中包含该字符串# 匹配失败返回Nonevalue=pattern.search(string[, start[, end]])# 3.全文匹配# 从目标字符串中查询所有符合匹配规则的字符,并存储到一个列表中# 匹配结束返回列表,包含匹配到的数据# 没有匹配到数据返回空列表,否则返回包含所有匹配数据的列表value_list=pattern.findall(string[, start[, end]])# 4.全文匹配获取迭代器# 从目标字符串中查询所有符合匹配规则的字符,并存储到一个迭代器中value_iter=pattern.finditer(string[, start[, end]])# 5.字符串切割:根据指定的正则表达式切割目标字符串并返回切割后的列表value_list=pattern.split(string)# 6.字符串替换:根据指定的匹配规则,将string中符合的字符串替换为value值,count是替换次数,默认全部替换value_replace=pattern.sub(value, string[, count])

通过正则匹配到的值对象value,可以通过指定的函数输出匹配到的数据的信息

# 输出匹配到的数据value.group()# 输出匹配到的第一组数据value.group(1)# 输出匹配的第n组数据:前提条件是在正则表达式中使用圆括号进行了n次分组value.group(n)# 输出匹配数据的索引范围value.span()# 输出匹配的第n组数据的索引范围value.span(n)# 输出匹配的第n组数据的索引开始位置value.start(n)# 输出匹配的第n组数据的索引结束位置value.end(n)

注意的是:在使用正则表达式时,贪婪模式和懒惰模式的操作行为可以精确的匹配数据

通常情况下,正则表达式模式是贪婪模式进行匹配的,如果需要精确匹配的情况下,在正常正则表达式后面添加一个?匹配符号即可!

# 定义目标字符串>>> s="helelo world"# 编译正则匹配对象:这里我们只是想得到: lel>>> pattern=re.compile('l*l')# 进行数据匹配操作>>> r=re.compile('l.*l')# 展示数据>>> r.search(s).group()# 展示得到的数据,显然匹配的数据中包含了其他数据'lelo worl'# 那么,下面这个例子,貌似更加实际>>> html="<div>i am div</div><p>i am p</p><div>i am div too</div>"# 定义匹配规则,只是想匹配div中包含的数据>>> pattern=re.compile("<div>.*</div>")# 打印展示数据>>> pattern.search(html).group()# 显示的数据,明显包含了不需要的数据,这是贪婪模式'<div>i am div</div><p>i am p</p><div>i am div</div>'# 重新定义>>> html="<div>i am div</div><p>i am p</p><div>i am div too</div>"# 修改过的正则表达式>>> pattern=re.compile("<div>.*?</div>")# 匹配得到数据>>> pattern.search(html).group()# 显示的数据,包含的数据,就是精确匹配到的数据'<div>i am div</div>'

正则表达式案例操作:百度图片搜索下载


4. Xpath

Xpath原本是在可扩展标记语言XML中进行数据查询的一种描述语言,可以很方便的在XML文档中查询到具体的数据;后续再发展过程中,对于标记语言都有非常友好的支持,如超文本标记语言HTML。


在操作Xpath之前,首先需要了解一些基础的技术术语

下面是一段常见的HTML代码

<html> <head>
 <title>文档标题</title>
 </head>
 <body> <h1>一级标题<h1>
 <table>
 <tr>
 <th>标题</th>
 <th>标题</th>
 <th>标题</th>
 <th>标题</th>
 </tr>
 <tr>
 <td>内容</td>
 <td>内容</td>
 <td>内容</td>
 <td>内容</td>
 </tr>
 </table>
 </body></html>
  • 根标签:在标记语言中,处在最外层的一个标签就是根标签,根标签有且仅有一个,在上述代码中<html>就是跟标签

  • 父标签:和子标签对应,内部包含了其他元素数据,该标签就是内部标签的父标签,如<html>是<head>的父标签,<head>又是<title>的父标签,某些说法中,父标签的父标签..被称为上级标签或则先代标签或者先辈标签

  • 子标签;和父标签对应,被包含的元素,就是外部元素的子标签,如<head>是<html>的子标签,<title>标签是<head>的子标签,<tr>是<table>的子标签;同样的子标签的子标签,也被称为后代标签

  • 兄弟标签:两个或者多个处在相同级别的标签,有相同的父标签,如<h1>和<table>是兄弟标签,<head>和<body>是兄弟标签,<table>中的两个<tr>是兄弟标签等等


  • Xpath描述语言的常见语法

    和正则表达式相比较,Xpath使用最简单的语法操作完成数据的查询匹配操作

表达式描述
nodename选取此节点的所有子节点。
/从根节点选取。
//从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
.选取当前节点。
..选取当前节点的父节点。
@选取属性。
*匹配任何元素节点。
@*匹配任何属性节点。
node()匹配任何类型的节点。

通过如下的方式直接操作上面的文档

路径表达式结果
html选取 html 元素的所有子节点。
/html选取根元素 html。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!
table/tr/td选取属于 table 的子元素的所有 td 元素。
//div | //table选取所有的div或者table节点
//table选取所有 table 子元素,而不管它们在文档中的位置。
html//div选择属于html元素的后代的所有div元素,而不管它们位于 html之下的什么位置。
//@href选取名为href 的所有属性。

标签条件筛选查询匹配

路径表达式结果
//table/tr[1]选取属于table子元素的第一个 tr 元素。
//table/tr[last()]选取属于 table 子元素的最后一个 tr 元素。
//table/tr[last()-1]选取属于 table 子元素的倒数第二个 tr 元素。
//table/tr[position()<3]选取最前面的两个属于 table 元素的子元素的tr元素。
//td[@width]选取所有拥有名为 width 的属性的 td 元素。
//td[@width='100']选取所有 td 元素,且这些元素拥有属性width并且值为100。
//tr//td[span>10000]选取tr元素的所有td子元素,并且其中的span 元素的值须大于10000。

同样,Xpath支持数据运算操作

运算符描述实例返回值
+加法6 + 410
-减法 6 - 42
*乘法 6 * 424
div除法 8 div 42
=等于price=9.80如果 price 是 9.80,则返回 true。如果 price 是 9.90,则返回 false。
!=不等于price!=9.80如果 price 是 9.90,则返回 true。如果 price 是9.80,则返回 false。
<小于price<9.80如果 price 是 9.00,则返回 true。如果 price 是 9.90,则返回 false。
<=小于或等于price<=9.80如果 price 是 9.00,则返回 true。如果 price 是 9.90,则返回 false。
>大于price>9.80如果 price 是 9.90,则返回 true。如果 price 是 9.80,则返回 false。
>=大于或等于price>=9.80如果 price 是 9.90,则返回 true。如果 price 是 9.70,则返回 false。
orprice=9.80 or price=9.70如果 price 是 9.80,则返回 true。如果 price 是 9.50,则返回 false。
andprice>9.00 and price<9.90如果 price 是 9.80,则返回 true。如果 price 是 8.50,则返回 false。
mod计算除法的余数5 mod 21

xpath在浏览器中进行测试时,可以给谷歌浏览器安装一个插件Xpath Helper插件;就可以直接在浏览器中通过xpath语法来完成对数据的匹配测试

测试通过的xpath语法,就可以直接在程序中使用了!


5.python操作Xpath

python第三方模块lxml可以对Xpath有友好的支持,lxml是C实现的一种高性能python用于HTML/XML的解析模块,可以通过Xpath语法在html文档数据中进行指定表达式数据的索引查询

  • 简单etree操作

# -*- coding:utf-8 -*-from lxml import etree# 模拟得到爬虫数据content="""
 <html>
 <head>
 <title>大牧</title>
 </head>
 <body>
 <h1>个人简介</h1>
 <div>
 <p>姓名:某某某</p>
 <p>住址:中国 乡下</p>
 <p>座右铭:岂能尽如人意,但求无愧于心</p>
 </div>
 </body>
 </html>
"""# 转换成html数据# html=etree.parse("index.html")# 从文件中直接加载html数据html=etree.HTML(content)# 通过etree.HTML()函数将字符串转换成HTML文档对象print dir(html)# 查看文档对象的所有函数print html.getchildren()# 查看文档对象根节点的所有子节点# 转换成字符数据str_html=etree.tostring(html)# 将HTML文档对象转换成字符串print type(str_html)# 查看输出类型print str_html# 查看输出数据
  • xpath操作

# -*- coding:utf-8 -*-from lxml import etree# 模拟得到爬虫数据content=u"""
 <html>
 <head>
 <title>大牧</title>
 </head>
 <body>
 <h1 name="title">个人简介</h1>
 <div name="desc">
 <p name="name">姓名:<span>某某某</span></p>
 <p name="addr">住址:中国 乡下</p>
 <p name="info">座右铭:岂能尽如人意,但求无愧于心</p>
 </div>
 </body>
 </html>
"""# 将爬取到的数据转换成HTML文档html=etree.HTML(content)# 查询所有的p标签p_x=html.xpath("//p")
print(p_x)# 查询所有Name属性的值v_attr_name=html.xpath("//@name")
print(v_attr_name)# 查询所有包含name属性的标签e_attr_name=html.xpath("//*[@name]")
print(e_attr_name)# 查询所有包含name属性,并且name属性值为desc的标签e_v_attr_name=html.xpath("//*[@name='desc']")
print(e_v_attr_name)# 查询所有p标签的文本内容,不包含子标签p_t=html.xpath("//p")for p in p_t: print (p.text)# 查询多个p标签下的所有文本内容,包含子标签中的文本内容p_m_t=html.xpath("//p")for p2 in p_m_t:
 print(p2.xpath("string(.)"))

案例操作:爬虫智联招聘中前10页的某个工作岗位名称、薪水、公司信息


6. BeautifulSoup4

BeautifulSoup也是一种非常优雅的专门用于进行HTML/XML数据解析的一种描述语言,可以很好的分析和筛选HTML/XML这样的标记文档中的指定规则数据

在数据筛选过程中其基础技术是通过封装HTML DOM树实现的一种DOM操作,通过加载网页文档对象的形式,从文档对象模型中获取目标数据

BeautifulSoup操作简单易于上手,在很多对于数据筛选性能要求并不是特别苛刻的项目中经常使用,目前市场流行的操作版本是BeautifulSoup4,经常称BS4

Xpath和BeautifulSoup

Xpath和BeautifulSoup都是基于DOM的一种操作模式

不同点在于加载文档对象模型DOM时出现的文档节点遍历查询操作过程,Xpath在进行遍历操作时针对描述语言指定的语法结构进行局部DOM对象树的遍历得到具体的数据,但是BS4在操作过程中,会将整个文档树进行加载然后进行查询匹配操作,使用过程中消耗资源较多,处理性能相对Xpath较低

那么为什么要用BS4呢?因为,它,足够简单!

描述语言处理效率上手程度
正则表达式效率非常高困难
Xpath效率很高正常
BS4效率较高简单

BS4本身是一种对描述语言进行封装的函数操作模块,通过提供面向对象的操作方式将文档对象中的各种节点、标签、属性、内容等等都封装成了python中对象的属性,在查询操作过程中,通过调用指定的函数直接进行数据 匹配检索操作,非常的简单非常的灵活。

一般BS4将HTML文档对象会转换成如下四种类型组合的文档树

  • Tag:标签对象

  • NavigableString:字符内容操作对象

  • BeautifulSoup:文档对象

  • Comment:特殊类型的NavigableString

说道这里,其实都是太多的理论性语法,BS4不同于正则和Xpath,没有什么基础语法结构,它封装的对象以及对象的属性操作,才是BS4不同凡响的核心价值

let's 上干货

7. python操作BeautifulSoup4

python中对于BeautifulSoup的支持,通过安装第三方模块来发挥它最好的操作

$ pip install beautifulsoup4
  • 入门第一弹:了解BeautifulSoup4

# coding:utf-8# 引入解析模块BS4from bs4 import BeautifulSoup# 从文件中加载html网页,指定HTML解析器使用lxml# 默认不指定的情况下,BS4会自动匹配当前系统中最优先的解析器soup=BeautifulSoup(open("index.html"), "lxml")# 如果是爬虫获取到的字符数据,直接交给BS4就OK拉# soup=BeatufulSoup(spider_content, "lxml")# 打印BeautifulSoup文档对象,得到的是文档树内容print(soup)# 打印类型:<class 'bs4.BeautifulSoup'>print(type(soup))
  • 入门第二弹:操作标签、属性、内容

# coding:utf-8from bs4 import BeautifulSoup# 得到构建的文档对象soup=BeautifulSoup(open("index.html"), "lxml")# Tag操作# 1. 获取标签print(soup.title) # <title>文章标题</title>print(soup.p) # <p>姓名:<span id="name">大牧</span></p> # 只返回第一个匹配到的标签对象print(soup.span) # <span id="name">大牧</span># 2.获取标签的属性print(soup.p.attrs) # {}:得到属性和值的字典print(soup.span.attrs) # {'id': 'name'}:得到属性和值的字典print(soup.span['id']) # name:得到指定属性的值soup.span['id']="real_name"print(soup.span['id']) # real_name : 可以方便的在BS4中直接对文档进行修改# 3. 获取标签的内容print(soup.head.string) # 文章标题:如果标签中只有一个子标签~返回子标签中的文本内容print(soup.p.string) # None:如果标签中有多个子标签,返回Noneprint(soup.span.string) # 大牧:直接返回包含的文本内容
  • 入门第三弹:操作子节点

# coding:utf-8# 引入BS4操作模块from bs4 import BeautifulSoup# 加载网页文档,构建文档对象soup=BeautifulSoup(open("index.html"), "lxml")
print(dir(soup))
print(soup.contents)# 得到文档对象中所有子节点print(soup.div.contents)# 得到匹配到的第一个div的子节点列表print(soup.div.children)# 得到匹配到的第一个div的子节点列表迭代器# for e1 in soup.div.children:# print("-->", e1)print(soup.div.descendants)# 得到匹配到的第一个div的子节点迭代器,所有后代节点单独一个一个列出# for e2 in soup.div.descendants:# print("==>", e2)
  • 入门第四弹: 面向对象的DOM匹配

# coding:utf-8# 引入BS4模块from bs4 import BeautifulSoup# 加载文档对象soup=BeautifulSoup(open("../index.html"), "lxml")# DOM文档树查询# 核心函数~请对比javasript dom结构了解它的方法# 如:findAllPrevious()/findAllNext()/findAll()/findPrevious()/findNext()等等# findAll()为例# 1. 查询指定的字符串res1=soup.findAll("p")# 查询所有包含p字符的标签print(res1)# 2. 正则表达式import re
res2=soup.findAll(re.compile(r"d+"))# 查询所有包含d字符的标签print(res2)# 3. 列表:选择res3=soup.findAll(["div", "h1"])# 查询所有的div或者h1标签print(res3)# 4. 关键字参数res4=soup.findAll(id="name")# 查询属性为id="name"的标签print(res4)# 5. 内容匹配res5=soup.findAll(text=u"男")# 直接匹配内容中的字符,必须保证精确匹配print(res5)
res6=soup.findAll(text=[u"文章标题", u"大牧"])# 查询包含精确内容的所有的标签print(res6)
res7=soup.findAll(text=re.compile(u"大+"))# 通过正则表达式进行模糊匹配print(res7)
  • 入门第五弹: 又见CSS