抓取网站数据入库详解,附图文
生活随笔
收集整理的這篇文章主要介紹了
抓取网站数据入库详解,附图文
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
抓取網站數據入庫詳解,附圖文
一. 分析需求
1.1 需求分析
- 剛好有這樣一個需求,去抓取下方網站的頁面全部數據,并存入MySQL數據庫。
- 這個頁面為: 爬取頁面
- 年月日選擇
- 出生于幾點,性別: 男或者女 選擇:
- 選擇年月日小時,性別后,跳轉的頁面(目標就是爬取此頁面):
1.2 分析實現可行性
- 經過對各個年份、月份、天、小時、男或女的點擊后進入的頁面發現如下特點:
- 頁面數據是靜態數據,并非從后端讀取得到 (可考慮有哪些技術可以實現)
- 頁面數據有固定的key:value屬性,比如 生肖: 牛,星座:雙魚座,且每個頁面的key,value是固定的,簡單來說,每個頁面的key都是一樣的,只是具體的value是根據年月日小時,性別會相應變動 (可考慮入庫的時候,對數據庫字段的定義)
- 頁面的路徑是有規律的。比如1950年1月1日0時,性別為女的,它的路徑為:http://www.8gua.cn/huashengsuanming/1950/w-1950-1-1-0.html,所以分析出路徑如下特點:
- 路徑為: url/年/(男為m,女為w)性別-年-月-日-時.html組成;
- 可選出生的小時為:
- 頁面路徑與可選的小時有著一一對應的關系;
二. 分析技術
- 解析靜態頁面,我們可以使用Jsoup來進行解析,它可以將頁面中的元素內容加載為Document文檔,我們可以操作指定;
- Jsoup是什么? jsoup 是一款Java 的HTML解析器,可直接解析某個URL地址、HTML文本內容。它提供了一套非常省力的API,可通過DOM,CSS以及類似于jQuery的操作方法來取出和操作數據。
- 通過Jsoup,我們可以訪問指定的頁面,抓取其中的內容,解析為文本(抓取數據文本)
- 提取關鍵詞key和value,我們可以使用正則表達式,將符合規則的數據截取出來(對數據文本的提取)
- 期間還需要用到:
- 獲取指定年、月有多少天的方法(對日期的處理)
- 獲取指定范圍內的集合(如1950年~2019年的集合,1到12月的集合等)
去實現一個功能時,先逐個分析用到哪些東西可以實現。由點到面,這樣一個大的功能就可以落地了。
三. 業務流程梳理
- 流程圖:
- 具體實現總結:我們先進行年月日時的遍歷,然后選擇男或女,這樣能夠獲得1950年~2019年每一天中固定的那幾個小時范圍的男或女的數據;遍歷最深層中寫邏輯代碼,在最里面寫:
- 通過年月日時,男或女,以及分頁等條件去拼接url
- 通過Jsoup獲取指定url內的數據,將其主體內容轉為文本,并過濾掉不需要的內容;
- 通過正則表達式,將里面的數據進行提取,變為key:value形式;
- 將結果封裝到對象中,然后存入數據庫;
四. 實戰代碼
4.1 公共方法以及依賴的引入
- 實戰代碼示例,文中代碼是用Kotlin編寫,與Java相差不大。
- 引入Jsoup依賴: compile group: 'org.jsoup', name: 'jsoup', version: '1.13.1'
Java版本依賴可直接搜索jsoup ,去尋找Maven依賴即可
- 獲取指定年、月下的最大天數: /*** 根據年 月 獲取對應的月份 天數*/fun getDaysByYearMonth(year: Int, month: Int): Int {val a = Calendar.getInstance()a[Calendar.YEAR] = yeara[Calendar.MONTH] = month - 1a[Calendar.DATE] = 1a.roll(Calendar.DATE, -1)return a[Calendar.DATE]}
- 獲取指定字符串范圍內的數據(包含范圍數據): /*** 獲取從pre 開始 ,從post結束的字符串數據*/fun parseTextAll(content: String, pre: String, post: String): String {// 查找的字符串//正則表達式val pattern = "$pre(.*?)$post"; //Java正則表達式以括號分組,第一個括號表示以"(乙方):"開頭,第三個括號表示以" "(空格)結尾,中間括號為目標值,// 創建 Pattern 對象val r = Pattern.compile(pattern);// 創建 matcher 對象val m = r.matcher(content);while (m.find()) {// 自動遍歷打印所有結果 group方法打印捕獲的組內容,以正則的括號角標從1開始計算,我們這里要第2個括號里的// 值, 所以取 m.group(2), m.group(0)取整個表達式的值,如果越界取m.group(4),則拋出異常return m.group(0)}return ""}
- 獲取指定字符串范圍內的數據(不包含范圍數據):/*** 獲取從pre 開始 ,從post結束的字符串數據,排除pre/post*/ fun parseText(content: String, pre: String, post: String): String {// 查找的字符串val pattern = "$pre(.*?)$post"// 創建 Pattern 對象val r = Pattern.compile(pattern);// 創建 matcher 對象val m = r.matcher(content);while (m.find()) {// 自動遍歷打印所有結果 group方法打印捕獲的組內容,以正則的括號角標從1開始計算,我們這里要第2個括號里的// 值, 所以取 m.group(2), m.group(0)取整個表達式的值,如果越界取m.group(4),則拋出異常return m.group(0).replace(pre, "").replace(post, "")}return "" }
- Jsoup根據指定url路徑分析頁面的文本內容: fun parseContent(urls: List<String>): String {val builder = StringBuilder()urls.forEach {try {val document: Document = Jsoup.parse(URL(it), 3 * 1000)builder.append(document.getElementsByTag("p").text())} catch (e: Exception) {}}return builder.toString()}
此處代碼 document.getElementsByTag().text() 為獲取指定標簽名的文本數據。這里則為獲取<p>標簽內的全部內容;
- 根據指定范圍的值獲取范圍內的集合: fun getListByRange(startInt: Int, endInt: Int): List<Int>{val rangeList = mutableListOf<Int>()var startCount= startIntwhile (startCount <= endInt){rangeList.add(startCount++)}return rangeList }
4.2 數據庫及存儲相關的設計
- 數據庫設計如下:
此處content_text為存儲的全部內容的文本信息,因為數據量較大,且可能此字段使用率不高,博主暫時將其放棄,不為此字段賦值;
- Java中的配置:
- 我們使用的持久層框架為: Mybatis-plus
- 數據庫此表名稱為:professional_letter
- 創建的Mapper: interface ProfessionalLetterMapper : BaseMapper<ProfessionalLetterEntity>
- 創建的Entity:@TableName("professional_letter") class ProfessionalLetterEntity{@ApiModelProperty("主鍵id")var id: Long? = 0L@ApiModelProperty("所屬年月日-時分秒,開始時間")var startTime: Date?=null@ApiModelProperty("所屬年月日-時分秒,結束時間")var endTime: Date?=null@ApiModelProperty("性別")var sex: Short?=null@ApiModelProperty("標題")var title: String?=null@ApiModelProperty("陽歷")var solarCalendar: String?=null@ApiModelProperty("農歷")var lunarCalendar: String?=null@ApiModelProperty("節氣")var solarTerms: String?=null@ApiModelProperty("星座")var constellation: String?= null@ApiModelProperty("十二生肖")var chineseZodiac: String?=null@ApiModelProperty("二十八星宿")var twentyEightNights: String?=null@ApiModelProperty("命主福元")var fortune: String?=null@ApiModelProperty("文本版內容")var contentText: String?=null@ApiModelProperty("json版本內容")var contentJson: String?=null@ApiModelProperty("胎元")var foetus: String?=null@ApiModelProperty("命宮")var mingGong: String?=null@ApiModelProperty("起大運周歲")var qiDaYun: String?=null }
- 創建的ContentJson(用于保存解析后的全部字段數據)import com.sino.hardware.common.JsonSerializable import io.swagger.annotations.ApiModelProperty class ContentJson : JsonSerializable() {@ApiModelProperty("陽歷")var solarCalendar: String? = null@ApiModelProperty("農歷")var lunarCalendar: String? = null@ApiModelProperty("節氣")var solarTerms: String? = null@ApiModelProperty("起大運周歲")var qiDaYun: String? = null@ApiModelProperty("星座")var constellation: String? = null@ApiModelProperty("十二生肖")var chineseZodiac: String? = null@ApiModelProperty("二十八星宿")var twentyEightNights: String? = null@ApiModelProperty("命主福元")var fortune: String? = null@ApiModelProperty("八字納音")var baZiNaYin: String? = null@ApiModelProperty("排大運")var paiDaYun: String? = null@ApiModelProperty("排流年")var paiLiuNian: String? = null@ApiModelProperty("胎元")var foetus: String? = null@ApiModelProperty("命宮")var mingGong: String? = null@ApiModelProperty("終身卦")var zhongShenGua: String? = null@ApiModelProperty("吉神兇煞")var jiShenXiongSha: String? = null@ApiModelProperty("吉神兇煞提示")var jiShenXiongShaTiShi: String? = null@ApiModelProperty("命局生克制化")var mingJuShengKeZhiHua: String? = null@ApiModelProperty("日主綜得分")var riZhuZhongDeiFen: String? = null@ApiModelProperty("日主綜得分提示")var riZHuZhongDeiFenTiShi: String? = null@ApiModelProperty("三命通會論斷")var sanMingTongHuiLunDuan: String? = null@ApiModelProperty("窮通寶鑒-調候用神參考")var qiongTongBaoJian: String? = null@ApiModelProperty("十神定位論斷")var shiShenDingWeiLunDuan: String? = null@ApiModelProperty("八字重量")var baZiZhongLiang: String? = null@ApiModelProperty("八字重量提示")var baZiZhongLiangTiShi: String? = null@ApiModelProperty("命宮寓意")var mingGongYuYi: String? = null@ApiModelProperty("性格特征")var xingGeTeZheng: String? = null@ApiModelProperty("性格特征提示")var xingGeTeZhengTiShi: String? = null@ApiModelProperty("職業財運")var zhiYeCaiYun: String? = null@ApiModelProperty("功名官運")var gongMingGuanYun: String? = null@ApiModelProperty("婚姻擇偶")var hunYingZeOu: String? = null@ApiModelProperty("配偶方向")var peiOuFangXiang: String? = null@ApiModelProperty("配偶方向提示:")var peiOuFangXiangTiShi: String? = null@ApiModelProperty("祖業遺產")var zuYeYiChan: String? = null@ApiModelProperty("體質健康")var tiZhiJianKang: String? = null@ApiModelProperty("體質健康提示")var tiZhiJianKangTiShi: String? = null@ApiModelProperty("有利選擇")var youLiXuanZe: String? = null@ApiModelProperty("流年")var liuNianMap: Map<String,String>? = null@ApiModelProperty("起大運運勢")var qiDaYunMap: Map<String,String>? = null}
4.3 核心代碼
五. 啟動后的注意點
- 所有的都準備好了,我們點擊啟動,就可以自動去抓取,并且入庫了:
- 數據已成功陸續插入:
- 最后感言:
- 因為數據量較大,我們可以放在服務器里進行執行;
- 我們也可以做優化,比如分庫分表之類,后期可根據實際需求來
- 我們可以做多線程等,同時執行;(這里的同時執行可以每個階段一個線程來存入,充分利用現代CPU的多核性能)
- 解決需求的時候,可根據實際需求來選擇技術方案,沒有哪種技術方案可以適用于所有需求。
總結
以上是生活随笔為你收集整理的抓取网站数据入库详解,附图文的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: EasyExcel 实现写入多个shee
- 下一篇: 国家开放大学2023春《马克思主义基本原