取元素的個數
$("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等等
第三,對于不同的數據,進行有效數據篩選時,應該分別怎么進行操作呢
非結構化數據:由于沒有任何固定的格式,只能根據字符出現的規律進行動態匹配的方式來完成數據的提?。?strong>正則表達式
結構化數據:由于數據本身存在一定的規律性,可以通過針對這些規律的分析工具進行數據的提取:正則表達式、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?!皁{1,}”等價于“o+”?!皁{0,}”則等價于“o*”。 |
{n,m} | m和n均為非負整數,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”將匹配“fooooood”中的前三個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 | 匹配非單詞邊界?!癳r\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("http://p") print(p_x)# 查詢所有Name屬性的值v_attr_name=html.xpath("http://@name") print(v_attr_name)# 查詢所有包含name屬性的標簽e_attr_name=html.xpath("http://*[@name]") print(e_attr_name)# 查詢所有包含name屬性,并且name屬性值為desc的標簽e_v_attr_name=html.xpath("http://*[@name='desc']") print(e_v_attr_name)# 查詢所有p標簽的文本內容,不包含子標簽p_t=html.xpath("http://p")for p in p_t: print (p.text)# 查詢多個p標簽下的所有文本內容,包含子標簽中的文本內容p_m_t=html.xpath("http://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小時內與您取得聯系。