jsoup是一款Java的HTML解析器,主要用來對HTML解析。官網(wǎng) 中文文檔
在爬蟲的時候,當(dāng)我們用HttpClient之類的框架,獲取到網(wǎng)頁源碼之后,需要從網(wǎng)頁源碼中取出我們想要的內(nèi)容,
就可以使用jsoup這類HTML解析器了??梢苑浅]p松的實現(xiàn)。
雖然jsoup也支持從某個地址直接去爬取網(wǎng)頁源碼,但是只支持HTTP,HTTPS協(xié)議,支持不夠豐富。
所以,主要還是用來對HTML進行解析。
◆其中,要被解析的HTML可以是一個HTML的字符串,可以是一個URL,可以是一個文件。
org.jsoup.Jsoup把輸入的HTML轉(zhuǎn)換成一個org.jsoup.nodes.Document對象,然后從Document對象中取出想要的元素。
org.jsoup.nodes.Document繼承了org.jsoup.nodes.Element,Element又繼承了org.jsoup.nodes.Node類。里面提供了豐富的方法來獲取HTML的元素。
◇從URL獲取HTML來解析
Document doc = Jsoup.connect("http://www.baidu.com/").get();
String title = doc.title();
其中Jsoup.connect("xxx")方法返回一個org.jsoup.Connection對象。
在Connection對象中,我們可以執(zhí)行g(shù)et或者post來執(zhí)行請求。但是在執(zhí)行請求之前,
我們可以使用Connection對象來設(shè)置一些請求信息。比如:頭信息,cookie,請求等待時間,代理等等來模擬瀏覽器的行為。
Document doc = Jsoup.connect("http://example.com")
.data("query", "Java")
.userAgent("Mozilla")
.cookie("auth", "token")
.timeout(3000)
.post();
◆獲得Document對象后,接下來就是解析Document對象,并從中獲取我們想要的元素了。
Document中提供了豐富的方法來獲取指定元素。
◇使用DOM的方式來取得
getElementById(String id):通過id來獲取
getElementsByTag(String tagName):通過標簽名字來獲取
getElementsByClass(String className):通過類名來獲取
getElementsByAttribute(String key):通過屬性名字來獲取
getElementsByAttributeValue(String key, String value):通過指定的屬性名字,屬性值來獲取
getAllElements():獲取所有元素
◇通過類似于css或jQuery的選擇器來查找元素
使用的是Element類的下記方法:
public Elements select(String cssQuery)
通過傳入一個類似于CSS或jQuery的選擇器字符串,來查找指定元素。
例子:
File input = new File("/tmp/input.html");
Document doc = Jsoup.parse(input, "UTF-8", "http://example.com/");
Elements links = doc.select("a[href]"); //帶有href屬性的a元素
Elements pngs = doc.select("img[src$=.png]");
//擴展名為.png的圖片
Element masthead = doc.select("div.masthead").first();
//class等于masthead的div標簽
Elements resultLinks = doc.select("h3.r > a"); //在h3元素之后的a元素
選擇器的更多語法(可以在org.jsoup.select.Selector中查看到更多關(guān)于選擇器的語法):
tagname: 通過標簽查找元素,比如:a
ns|tag: 通過標簽在命名空間查找元素,比如:可以用 fb|name 語法來查找 <fb:name> 元素
#id: 通過ID查找元素,比如:#logo
.class: 通過class名稱查找元素,比如:.masthead
[attribute]: 利用屬性查找元素,比如:[href]
[^attr]: 利用屬性名前綴來查找元素,比如:可以用[^data-] 來查找?guī)в蠬TML5 Dataset屬性的元素
[attr=value]: 利用屬性值來查找元素,比如:[width=500]
[attr^=value], [attr$=value], [attr=value]: 利用匹配屬性值開頭、結(jié)尾或包含屬性值來查找元素,比如:[href=/path/]
[attr~=regex]: 利用屬性值匹配正則表達式來查找元素,比如: img[src~=(?i).(png|jpe?g)]
*: 這個符號將匹配所有元素
Selector選擇器組合使用
el#id: 元素+ID,比如: div#logo
el.class: 元素+class,比如: div.masthead
el[attr]: 元素+class,比如: a[href]
任意組合,比如:a[href].highlight
ancestor child: 查找某個元素下子元素,比如:可以用.body p 查找在"body"元素下的所有 p元素
parent > child: 查找某個父元素下的直接子元素,比如:可以用div.content > p 查找 p 元素,也可以用body > * 查找body標簽下所有直接子元素
siblingA + siblingB: 查找在A元素之前第一個同級元素B,比如:div.head + div
siblingA ~ siblingX: 查找A元素之前的同級X元素,比如:h1 ~ p
el, el, el:多個選擇器組合,查找匹配任一選擇器的唯一元素,例如:div.masthead, div.logo
偽選擇器selectors
:lt(n): 查找哪些元素的同級索引值(它的位置在DOM樹中是相對于它的父節(jié)點)小于n,比如:td:lt(3) 表示小于三列的元素
:gt(n):查找哪些元素的同級索引值大于n,比如: div p:gt(2)表示哪些div中有包含2個以上的p元素
:eq(n): 查找哪些元素的同級索引值與n相等,比如:form input:eq(1)表示包含一個input標簽的Form元素
:has(seletor): 查找匹配選擇器包含元素的元素,比如:div:has(p)表示哪些div包含了p元素
:not(selector): 查找與選擇器不匹配的元素,比如: div:not(.logo) 表示不包含 class="logo" 元素的所有 div 列表
:contains(text): 查找包含給定文本的元素,搜索不區(qū)分大不寫,比如: p:contains(jsoup)
:containsOwn(text): 查找直接包含給定文本的元素
:matches(regex): 查找哪些元素的文本匹配指定的正則表達式,比如:div:matches((?i)login)
:matchesOwn(regex): 查找自身包含文本匹配指定正則表達式的元素
注意 :上述偽選擇器索引是從0開始的,也就是說第一個元素索引值為0,第二個元素index為1等
◆通過上面的選擇器,我們可以取得一個Elements對象,它繼承了ArrayList對象,里面放的全是Element對象。
接下來我們要做的就是從Element對象中,取出我們真正需要的內(nèi)容。
通常有下面幾種方法:
◇Element.text()
這個方法用來取得一個元素中的文本。
◇Element.html()或Node.outerHtml()
這個方法用來取得一個元素中的html內(nèi)容
◇Node.attr(String key)
獲得一個屬性的值,例如取得超鏈接<a href="">中href的值
綜合實例:采集開源中國項目信息
package com.company;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
public class Main {
public static void main(String[] args) throws IOException {
// write your code here
Set<String> setUrls = new HashSet<>();
for(int i = 1; i <= 5; i++)
{
String strUrl = "https://www.oschina.net/project/list?company=0&sort=score&lang=0&recommend=false&p="+i;
setUrls.add(strUrl);
}
Set<String> setProjUrls = new HashSet<>();
for(String stringUrl : setUrls)
{
Document document = Jsoup.connect(stringUrl)
.userAgent("Mozilla/5.0 (Windows NT 6.1; rv:30.0) Gecko/20100101 Firefox/30.0")
.get();
Elements elements1 = document.select(".news-list");
Elements elements = elements1.select("div.box");
for(Element element : elements)
{
Elements eleUrl = element.select("div.box-aw a");
String strPrjUrl = eleUrl.attr("href");
setProjUrls.add(strPrjUrl);
// System.out.println(strPrjUrl);
Elements eleTitle = eleUrl.select(".title");
String strTitle = eleTitle.text();
// System.out.println(strTitle);
Elements eleSummary = eleUrl.select(".summary");
String strSummary = eleSummary.text();
// System.out.println(strSummary);
}
}
for(String stringUrl : setProjUrls)
{
Document document = Jsoup.connect(stringUrl)
.userAgent("Mozilla/5.0 (Windows NT 6.1; rv:30.0) Gecko/20100101 Firefox/30.0")
.get();
Elements elements = document.select("div.box-aw a h1");
String strTitle = elements.text();
System.out.println("標題:" + strTitle);
Elements elementsSection = document.select("section.list");
int nSize = elementsSection.get(0).children().size();
if(nSize == 0)
continue;
Element elementProtocol = elementsSection.get(0).child(0);
Elements elesPro = elementProtocol.select("span");
String strPro = elesPro.text();
System.out.println("開源協(xié)議:" + strPro);
nSize--;
if(nSize == 0)
continue;
Element elementLan = elementsSection.get(0).child(1);
Elements elesLan = elementLan.select("span").get(0).children();
StringBuilder strlan = new StringBuilder();
for(Element ele : elesLan)
{
String strLanTemp = ele.text();
if(strLanTemp.indexOf("查看源碼")>=0)
break;
strlan.append(strLanTemp+",");
}
if(elesLan.size()>0)
{
String strLanguage = strlan.toString().substring(0,strlan.length()-1);
System.out.println("開發(fā)語言:" + strLanguage);
}
nSize--;
if(nSize == 0)
continue;
Element elementOS = elementsSection.get(0).child(2);
Elements elesOS = elementOS.select("span");
String strOS = elesOS.text();
System.out.println("操作系統(tǒng):" + strOS);
nSize--;
if(nSize == 0)
continue;
Element elementAuthor = elementsSection.get(0).child(3);
Elements elesAuthor = elementAuthor.select("a.link");
String strAuthor= elesAuthor.text();
System.out.println("軟件作者;" + strAuthor);
System.out.println("---------------------");
}
}
}
爬博客園
package com.neusoft;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args ) throws IOException {
// //真實地址
// Document document = Jsoup.connect("https://www.cnblogs.com/pick/").get();
// Elements divs = document.select("div.post_item_body .titlelnk");
// for (Element elements : divs){
// String href = elements.attr("href");
// System.out.println("文章標題:"+ elements.text() + "文章鏈接:"+href );
// }
String url="https://www.cnblogs.com/mvc/AggSite/PostList.aspx";
for (int i=1;i<=81;i++){
Map<String, String> param = new HashMap<>();
param.put("CategoryId","-2");
param.put("CategoryType","Picked");
param.put("ItemListActionName","PostList");
param.put("PageIndex",String.valueOf(i));
param.put("ParentCategoryId","0");
param.put("TotalPostCount","1612");
Document document = Jsoup.connect(url).data(param)
.userAgent("Mozilla")
.timeout(3000)
.post();
Elements divs = document.select("div.post_item div.post_item_body h3 a");
System.out.println(divs);
System.out.println("---------------------------------");
}
}
}
爬圖片
package com.neusoft;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
/**
* Created by wth on 2019/3/14.
*/
public class PicSpiderDemo {
public static void downloadPic(String filePath,String url) throws IOException {
File dir = new File(filePath);
if (!dir.exists()){
dir.mkdirs();
}
String[] arrs = url.split("/");
// 保存到磁盤上的圖片的文件名
String filename = arrs[arrs.length - 1];
// 根據(jù)url,讀取圖片的二進制流
URL url1 = new URL(url);
URLConnection connection = url1.openConnection();
connection.setConnectTimeout(10*1000);
InputStream inputStream = connection.getInputStream();
BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(filePath+filename));
byte[] bytes = new byte[10240];
int len = 0;
//-1
while ((len = inputStream.read(bytes)) != -1){
os.write(bytes,0,len);
}
inputStream.close();
os.close();
return;
}
public static void main(String[] args) throws IOException {
Document document = Jsoup.connect("http://www.5060w.net/Movie/").get();
Elements imgs = document.getElementsByTag("img");
for (Element element:imgs){
System.out.println(element.attr("abs:src"));
String picUrl = element.attr("abs:src");
PicSpiderDemo.downloadPic("e:/img",picUrl);
}
}
}
爬取騰訊首頁全部圖片
package com.company;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
public class meizi {
/**
* 下載圖片到指定目錄
*
* @param filePath 文件路徑
* @param imgUrl 圖片URL
*/
public static void downImages(String filePath, String imgUrl) {
// 若指定文件夾沒有,則先創(chuàng)建
File dir = new File(filePath);
if (!dir.exists()) {
dir.mkdirs();
}
// 截取圖片文件名
String fileName = imgUrl.substring(imgUrl.lastIndexOf('/') + 1, imgUrl.length());
try {
// 文件名里面可能有中文或者空格,所以這里要進行處理。但空格又會被URLEncoder轉(zhuǎn)義為加號
String urlTail = URLEncoder.encode(fileName, "UTF-8");
// 因此要將加號轉(zhuǎn)化為UTF-8格式的%20
imgUrl = imgUrl.substring(0, imgUrl.lastIndexOf('/') + 1) + urlTail.replaceAll("\\+", "\\%20");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// 寫出的路徑
File file = new File(filePath + File.separator + fileName);
try {
// 獲取圖片URL
URL url = new URL(imgUrl);
// 獲得連接
URLConnection connection = url.openConnection();
// 設(shè)置10秒的相應(yīng)時間
connection.setConnectTimeout(10 * 1000);
// 獲得輸入流
InputStream in = connection.getInputStream();
// 獲得輸出流
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
// 構(gòu)建緩沖區(qū)
byte[] buf = new byte[1024];
int size;
// 寫入到文件
while (-1 != (size = in.read(buf))) {
out.write(buf, 0, size);
}
out.close();
in.close();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
// 利用Jsoup獲得連接
Connection connect = Jsoup.connect("http://www.qq.com");
try {
// 得到Document對象
Document document = connect.get();
// 查找所有img標簽
Elements imgs = document.getElementsByTag("img");
System.out.println("共檢測到下列圖片URL:");
System.out.println("開始下載");
// 遍歷img標簽并獲得src的屬性
for (Element element : imgs) {
//獲取每個img標簽URL "abs:"表示絕對路徑
String imgSrc = element.attr("abs:src");
// 打印URL
System.out.println(imgSrc);
//下載圖片到本地
meizi.downImages("d:/img", imgSrc);
}
System.out.println("下載完成");
} catch (IOException e) {
e.printStackTrace();
}
}
}
解析json(悟空問答網(wǎng)案例)
package com.company;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import java.io.IOException;
/**
* Created by Administrator on 2018/8/8.
*/
public class hello {
public static void main(String[] args) throws IOException {
Connection.Response res = Jsoup.connect("https://www.wukong.com/wenda/web/nativefeed/brow/?concern_id=6300775428692904450&t=1533714730319&_signature=DKZ7mhAQV9JbkTPBachKdgyme4")
.header("Accept", "*/*")
.header("Accept-Encoding", "gzip, deflate")
.header("Accept-Language","zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3")
.header("Content-Type", "application/json;charset=UTF-8")
.header("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64; rv:48.0) Gecko/20100101 Firefox/48.0")
.timeout(10000).ignoreContentType(true).execute();//.get();
String body = res.body();
System.out.println(body);
JSONObject jsonObject2 = JSON.parseObject(body);
JSONArray jsonArray = jsonObject2.getJSONArray("data");
//JSONArray jsonArray1 = JSONArray.parseArray(JSON_ARRAY_STR);//因為JSONArray繼承了JSON,所以這樣也是可以的
//遍歷方式1
int size = jsonArray.size();
for (int i = 0; i < size; i++){
JSONObject jsonObject = jsonArray.getJSONObject(i);
if(jsonObject.containsKey("question"))
{
JSONObject jsonObject3 = jsonObject.getJSONObject("question");
String qid = jsonObject3.getString("qid");
System.out.println(qid);
}
}
}
}
fastjson補充
json字符串-數(shù)組類型與JSONArray之間的轉(zhuǎn)換
/**
* json字符串-數(shù)組類型與JSONArray之間的轉(zhuǎn)換
*/
public static void testJSONStrToJSONArray(){
JSONArray jsonArray = JSON.parseArray(JSON_ARRAY_STR);
//JSONArray jsonArray1 = JSONArray.parseArray(JSON_ARRAY_STR);//因為JSONArray繼承了JSON,所以這樣也是可以的
//遍歷方式1
int size = jsonArray.size();
for (int i = 0; i < size; i++){
JSONObject jsonObject = jsonArray.getJSONObject(i);
System.out.println(jsonObject.getString("studentName")+":"+jsonObject.getInteger("studentAge"));
}
//遍歷方式2
for (Object obj : jsonArray) {
JSONObject jsonObject = (JSONObject) obj;
System.out.println(jsonObject.getString("studentName")+":"+jsonObject.getInteger("studentAge"));
}
}
復(fù)雜json格式字符串與JSONObject之間的轉(zhuǎn)換
/**
* 復(fù)雜json格式字符串與JSONObject之間的轉(zhuǎn)換
*/
public static void testComplexJSONStrToJSONObject(){
JSONObject jsonObject = JSON.parseObject(COMPLEX_JSON_STR);
//JSONObject jsonObject1 = JSONObject.parseObject(COMPLEX_JSON_STR);//因為JSONObject繼承了JSON,所以這樣也是可以的
String teacherName = jsonObject.getString("teacherName");
Integer teacherAge = jsonObject.getInteger("teacherAge");
JSONObject course = jsonObject.getJSONObject("course");
JSONArray students = jsonObject.getJSONArray("students");
}
另一個實例(采集悟空問答某個問題的評論信息)
@Test
public void testWukongAnswer() throws IOException {
Document doc = Jsoup.connect("https://www.wukong.com/question/6586489850478723332/").get();
Elements elements = doc.select("div.answer-item");
for(Element element : elements)
{
Element elementImg = element.selectFirst(".answer-user-avatar img");
System.out.println(elementImg.attr("abs:src"));
Element elementName = element.selectFirst(".answer-user-name");
System.out.println(elementName.text());
Element elementDate = element.selectFirst(".answer-user-tag");
System.out.println(elementDate.text());
Element elementContent = element.selectFirst("div.answer-text-full");
System.out.println(elementContent.text());
}
}
爬掘金
package com.neusoft;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import java.io.IOException;
/**
* Created by wth on 2019/3/14.
*/
public class JsonDemo1 {
public static void main(String[] args) throws IOException {
Connection.Response res = Jsoup.connect("https://timeline-merger-ms.juejin.im/v1/get_entry_by_rank?src=web&before=18.710985665233&limit=20&category=all")
.header("Accept", "*/*")
.header("Accept-Encoding", "gzip, deflate")
.header("Accept-Language","zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3")
.header("Content-Type", "application/json;charset=UTF-8")
.header("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64; rv:48.0) Gecko/20100101 Firefox/48.0")
.timeout(10000).ignoreContentType(true).execute();//.get();
String body = res.body();
System.out.println(body);
JSONObject jsonObject = JSON.parseObject(body);
JSONObject jo = jsonObject.getJSONObject("d");
JSONArray entrylist = jo.getJSONArray("entrylist");
//遍歷方式2
for (Object obj : entrylist) {
jsonObject = (JSONObject) obj;
System.out.println(jsonObject.get("title"));
}
}
}
練習(xí)
爬取獵聘網(wǎng) java關(guān)鍵字 企業(yè)招聘信息(包含詳情)
https://www.liepin.com/
package com.neusoft;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.IOException;
public class Lieping {
public static void main(String[] args) throws IOException {
for (int i = 0; i <=100 ; i++) {
System.out.println("\n--------------第" + i + "頁-----------------");
Document doc = Jsoup.connect("https://www.liepin.com/zhaopin/?init=-1&headckid=b917758e7de3b9f8&fromSearchBtn=2&ckid=b917758e7de3b9f8°radeFlag=0&sfrom=click-pc_homepage-centre_searchbox-search_new&key=%E5%A4%A7%E6%95%B0%E6%8D%AE&siTag=LGV-fc5u_67LtFjetF6ACg%7EfA9rXquZc5IkJpXC-Ycixw&d_sfrom=search_fp&d_ckId=aca1a77f8138ed02c5e6fdd52ca1cc51&d_curPage=2&d_pageSize=40&d_headId=aca1a77f8138ed02c5e6fdd52ca1cc51&curPage="+i).get();
// System.out.println(doc);
Elements elements = doc.select("div.job-info");
for (Element ele:elements) {
System.out.println(ele.select("h3 a").text());
String href=ele.select("h3 a").attr("abs:href");//abs:取絕對路徑
Document doc2 = Jsoup.connect(href).get();
System.out.println(doc2.select("div.job-description div.content-word").text());
}
}
}
}