取元素的个数
$("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立即执行的函数)
、筛选请求
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/*:隐藏所有响应类型为图片的请求
二、修改请求参数之临时修改参数
三、断点修改请求参数
四、通过Composer修改参数
五、修改请求参数之永久修改参数,FiddlerScript修改参数
六、修改请求脚本代码
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);
}
if (oSession.HTTPMethodIs("POST") && oSession.uriContains("example.com")) {
// 获取请求的 Body 数据
var requestBody=oSession.GetRequestBodyAsString();
// 修改参数
requestBody=requestBody.replace("param1=value1", "param1=newValue");
// 更新请求的 Body 数据
oSession.utilSetRequestBody(requestBody);
}
if (oSession.HTTPMethodIs("GET") && oSession.uriContains("example.com")) {
// 获取请求的 URL
var url=oSession.fullUrl;
// 修改参数
url=url.replace("param1=value1", "param1=newvalue");
// 更新请求的 URL
oSession.fullUrl=url;
}
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;
}
if (oSession.HTTPMethodIs("GET") && oSession.uriContains("example.com")) {
// 获取请求的头部对象
var headers=oSession.oRequest.headers;
// 修改头部的值
headers["key"]="value";
// 删除指定的头部
headers.Remove("key");
}
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
}
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
}
FiddlerApplication.Log.LogString("cookie: " + cookieValue);
目录
正则表达式提取数据
正则表达式案例操作
Xpath提取数据
Xpath案例操作
BeautifulSoup4提取数据
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 + 4 | 10 |
- | 减法 6 - 4 | 2 | |
* | 乘法 6 * 4 | 24 | |
div | 除法 8 div 4 | 2 | |
= | 等于 | 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。 |
or | 或 | price=9.80 or price=9.70 | 如果 price 是 9.80,则返回 true。如果 price 是 9.50,则返回 false。 |
and | 与 | price>9.00 and price<9.90 | 如果 price 是 9.80,则返回 true。如果 price 是 8.50,则返回 false。 |
mod | 计算除法的余数 | 5 mod 2 | 1 |
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
*请认真填写需求信息,我们会在24小时内与您取得联系。