java網絡爬蟲入門
copy自:http://www.ayulong.cn/types/2 視頻教程:https://www.bilibili.com/video/BV1cE411u7RA?p=1
1. 網絡爬蟲簡介
網絡爬蟲也叫網絡機器人, 是一種可以按照一定規則自動采集互聯網信息的程序或腳本, 爬蟲一般分為數據采集, 處理, 儲存三個部分, 從若干初始網頁的URL開始抓取網頁, 不斷獲取頁面上的URL放入隊列直到滿足系統的一定條件停止
為什么要學習網絡爬蟲
1. 可以實現私人的搜索引擎
2. 大數據時代獲取數據源, 作數據分析
3. 可以更好地進行搜索引擎優化 (SEO)
4. 有利於就業, 爬蟲工程師需求量大, 發展空間廣
2. hello world
環境准備
JDK1.8
IntelliJ IDEA
DEA自帶的Maven
導入 pom.xml
<dependencies> <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient --> <!-- HttpClient --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.12</version> </dependency> <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 --> <!--日志--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.25</version> <scope>test</scope> </dependency> </dependencies>
創建 slf4j 日志配置文件
在 resources 目錄下創建 log4j.properties 文件, 並添加以下配置
# A1 在控制台顯示日志
log4j.rootLogger=DEBUG,A1
log4j.logger.cn.itcast = DEBUG
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH🇲🇲ss,SSS} [%t] [%c]-[%p] %m%n
最簡單的爬蟲程序
在java的 cn.ayulong.crawler.test 中創建 CrawlerFirst類
package cn.ayulong.crawler.test; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import java.io.IOException; public class CrawlerFirst { public static void main(String[] args) throws IOException { // 1. 打開瀏覽器, 創建 HttpClient 對象 CloseableHttpClient httpClient = HttpClients.createDefault(); // 2. 輸入網址, 發起get請求創建HttpGet對象 HttpGet httpGet = new HttpGet("http://www.baidu.com/"); // 3. 按回車, 發起請求, 返回響應 CloseableHttpResponse response = httpClient.execute(httpGet); // 4. 解析響應, 獲取數據 // 判斷狀態碼是否是 200 if (response.getStatusLine().getStatusCode() == 200) { HttpEntity httpEntity = response.getEntity(); String content = EntityUtils.toString(httpEntity, "utf-8"); System.out.println(content); } } } // 直接運行, 成功抓取到百度首頁html並打印到控制台
注意: 此方法只能抓取 http 協議的頁面, 如果想抓取 https 的頁面, 可以參考
3. HttpClient
HttpClient: Java 的 HTTP 協議客戶端, 用於抓取網頁數據
GET 請求
public class HttpGetTest { public static void main(String[] args) { // 創建HttpClient對象 CloseableHttpClient httpClient = HttpClients.createDefault(); // 創建HttpGet對象, 設置url訪問地址 HttpGet httpGet = new HttpGet("http://www.ayulong.cn"); CloseableHttpResponse response = null; try { // 使用 HttpClient 發起請求, 獲取 response response = httpClient.execute(httpGet); // 解析響應 if (response.getStatusLine().getStatusCode() == 200) { String content = EntityUtils.toString(response.getEntity(), "utf8"); System.out.println(content.length()); } } catch(IOException e) { e.printStackTrace(); } finally { // 關閉 response try { response.close(); httpClient.close(); } catch (IOException e) { e.printStackTrace(); } } } }
Get請求帶參數
public class HttpGetParamTest { public static void main(String[] args) throws Exception { // 創建HttpClient對象 CloseableHttpClient httpClient = HttpClients.createDefault(); // 設置請求地址是: http://yun.itheima.com/search?keys=Java // 創建URIBuilder URIBuilder uriBuilder = new URIBuilder("http://yun.itheima.com/search"); // 設置參數 uriBuilder.setParameter("keys", "Java"); // 創建HttpGet對象, 設置url訪問地址 HttpGet httpGet = new HttpGet(uriBuilder.build()); System.out.println("發起請求的信息: " + httpGet); CloseableHttpResponse response = null; try { // 使用 HttpClient 發起請求, 獲取 response response = httpClient.execute(httpGet); // 解析響應 if (response.getStatusLine().getStatusCode() == 200) { String content = EntityUtils.toString(response.getEntity(), "utf8"); } } catch(IOException e) { e.printStackTrace(); } finally { // 關閉 response try { response.close(); httpClient.close(); } catch (IOException e) { e.printStackTrace(); } } } }
Post請求帶參數
使用 HttpClient 發送不帶參數的 post 請求與 發送不帶參數的 get 請求類似, 只是 HttpGet 要改為 HttpPost, 所以只記錄帶參數的 post 請求方式
public class HttpPostParamTest { public static void main(String[] args) throws Exception { // 創建HttpClient對象 CloseableHttpClient httpClient = HttpClients.createDefault(); // 創建HttpPost對象, 設置url訪問地址 HttpPost httpPost = new HttpPost("http://yun.itheima.com/search"); // 聲明List集合, 封裝表單中的參數 ArrayList<NameValuePair> params = new ArrayList<NameValuePair>(); params.add(new BasicNameValuePair("keys", "Java")); // 創建表單的Entity對象, 第一個參數就是封裝好的表單數據, 第二個參數就是編碼 UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(params, "utf8"); // 設置表單的Entity對象到 Post 請求中 httpPost.setEntity(formEntity); CloseableHttpResponse response = null; try { // 使用 HttpClient 發起請求, 獲取 response response = httpClient.execute(httpPost); // 解析響應 if (response.getStatusLine().getStatusCode() == 200) { String content = EntityUtils.toString(response.getEntity(), "utf8"); System.out.println(content.length()); } } catch(IOException e) { e.printStackTrace(); } finally { // 關閉 response try { response.close(); httpClient.close(); } catch (IOException e) { e.printStackTrace(); } } } }
連接池
public class HttpClientPoolTest { public static void main(String[] args) { // 創建連接池管理器 PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); // 設置連接數 cm.setMaxTotal(100); // 設置每個主機的最大連接數 cm.setDefaultMaxPerRoute(10); // 使用連接池管理器發起請求 doGet(cm); doGet(cm); } private static void doGet(PoolingHttpClientConnectionManager cm) { // 不是每次創建新的HttpClient, 而是從連接池中獲取 HttpClient 對象 CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build(); HttpGet httpGet = new HttpGet("http://www.itcast.cn"); CloseableHttpResponse response = null; try { // 使用 HttpClient 發起請求, 獲取 response response = httpClient.execute(httpGet); // 解析響應 if (response.getStatusLine().getStatusCode() == 200) { String content = EntityUtils.toString(response.getEntity(), "utf8"); System.out.println(content.length()); } } catch(IOException e) { e.printStackTrace(); } finally { // 關閉 response if (response != null) { try { response.close(); } catch (IOException e) { e.printStackTrace(); } // 不能關閉 HttpClient, 由連接池管理 HttpClient // httpClient. close(); } } } }
請求參數配置
public class HttpConfigTest { public static void main(String[] args) { // 創建HttpClient對象 CloseableHttpClient httpClient = HttpClients.createDefault(); // 創建HttpGet對象, 設置url訪問地址 HttpGet httpGet = new HttpGet("http://yun.itheima.com/search?keys=Java"); // 配置請求信息 RequestConfig config = RequestConfig.custom().setConnectTimeout(1000) // 創建連接的最長時間, 單位是毫秒 .setConnectionRequestTimeout(500) // 設置獲取連接的最長時間, 單位是毫秒 .setSocketTimeout(10 * 1000) // 設置數據傳輸的最長時間, 單位是毫秒 .build(); // 給請求設置請求信息 httpGet.setConfig(config); CloseableHttpResponse response = null; try { // 使用 HttpClient 發起請求, 獲取 response response = httpClient.execute(httpGet); // 解析響應 if (response.getStatusLine().getStatusCode() == 200) { String content = EntityUtils.toString(response.getEntity(), "utf8"); System.out.println(content.length()); } } catch(IOException e) { e.printStackTrace(); } finally { // 關閉 response try { response.close(); httpClient.close(); } catch (IOException e) { e.printStackTrace(); } } } }
4. Jsoup
jsoup 是一款Java 的HTML解析器,可直接解析某個URL地址、HTML文本內容。它提供了一套非常省力的API,可通過DOM,CSS以及類似於jQuery的操作方法來取出和操作數據。
jsoup的主要功能如下:
-
從一個URL,文件或字符串中解析HTML;
-
使用DOM或CSS選擇器來查找、取出數據;
-
可操作HTML元素、屬性、文本;( Jsoup一般用於解析爬到的數據並存儲, 很少用到操作 )
先在pom中導入依賴, 搭建開發環境
<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup --> <!--Jsoup--> <dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.11.3</version> </dependency> <!-- https://mvnrepository.com/artifact/junit/junit --> <!--測試--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/commons-io/commons-io --> <!--操作文件--> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 --> <!--操作字符串--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.4
Jsoup解析URL
Jsoup可以直接輸入url,它會發起請求並獲取數據,封裝為Document對象
public class JsoupFirstTest { @Test public void testUrl() throws Exception { // 解析url地址, 第一個參數是訪問的url, 第二個參數時訪問時候的超時時間 Document doc = Jsoup.parse(new URL("http://www.itcast.cn"), 1000); // 使用標簽選擇器, 獲取title標簽中的內容 String title = doc.getElementsByTag("title").first().text(); // 打印 System.out.println(title); } }
PS:雖然使用Jsoup可以替代HttpClient直接發起請求解析數據,但是往往不會這樣用,因為實際的開發過程中,需要使用到多線程,連接池,代理等等方式,而jsoup對這些的支持並不是很好,所以我們一般把jsoup僅僅作為Html解析工具使用
Jsoup解析字符串
@Test public void testString() throws Exception { // 使用工具類讀取文件, 獲取字符串 String content = FileUtils.readFileToString(new File("D:\\360MoveData\\Users\\Administrator\\Desktop\\test.html"), "utf8"); // 解析字符串 Document doc = Jsoup.parse(content); String title = doc.getElementsByTag("title").first().text(); System.out.println(title); } }
Jsoup解析文字
@Test public void testFile() throws Exception { // 解析文件 Document doc = Jsoup.parse(new File("D:\\360MoveData\\Users\\Administrator\\Desktop\\test.html"), "utf8"); String title = doc.getElementsByTag("title").first().text(); System.out.println(title); }
使用Dom方式遍歷文檔
@Test public void testDom() throws Exception { // 解析文件件, 獲取Document對象 Document doc = Jsoup.parse(new File("D:\\360MoveData\\Users\\Administrator\\Desktop\\test.html"), "utf8"); // 1.根據id查詢元素getElementById Element element = doc.getElementById("city_bj"); // 2.根據標簽獲取元素getElementsByTag Elements spans = doc.getElementsByTag("span"); // 3.根據class獲取元素getElementsByClass Element a = doc.getElementsByClass("class_a class_b").first(); // 4.根據屬性獲取元素getElementsByAttribute Element abc = doc.getElementsByAttribute("abc").first(); // 5.根據屬性與屬性值篩選 Element href = doc.getElementsByAttributeValue("href", "http://sh.itcast.cn").first(); // 打印元素內容 System.out.println("獲取到的元素內容是: " + element.text()); for (Element span : spans) { System.out.println(span.text()); } System.out.println(a.text()); System.out.println("abc.text() = " + abc.text()); System.out.println("href.text() = " + href.text()); }
Selector選擇器組合使用
@Test public void testSelector2() throws Exception { // 解析html文件, 獲取Document對象 Document doc = Jsoup.parse(new File("D:\\360MoveData\\Users\\Administrator\\Desktop\\test.html"), "utf8"); //el#id: 元素+ID,比如: h3#city_bj //Element element = doc.select("h3#city_bj").first(); //el.class: 元素+class,比如: li.class_a //Element element = doc.select("li.class_a").first(); //el[attr]: 元素+屬性名,比如: span[abc] //Element element = doc.select("span[abc]").first(); //任意組合: 比如:span[abc].s_name Element element = doc.select("span[abc].s_name").first(); //ancestor child: 查找某個元素下子元素,比如:.city_con li 查找"city_con"下的所有li //Elements elements = doc.select(".city_con li"); //parent > child: 查找某個父元素下的直接子元素,比如: // .city_con > ul > li 查找city_con第一級(直接子元素)的ul,再找所有ul下的第一級li //Elements elements = doc.select(".city_con > ul > li"); //parent > *: 查找某個父元素下所有直接子元素 Elements elements = doc.select(".city_con > ul > *"); // 打印 System.out.println("獲取到的內容是: " + element.text()); for (Element ele : elements) { System.out.println("遍歷的結果: " + ele.text()); } }