Java如何解析html中的內(nèi)容并存到數(shù)據(jù)庫詳解
一、前言
最近接到一個任務,需要爬取五級行政區(qū)劃的所有數(shù)據(jù)(大概71萬條數(shù)據(jù)在),需要爬取的網(wǎng)站:行政區(qū)劃 - 行政區(qū)劃代碼查詢 發(fā)現(xiàn)這個網(wǎng)站不是用接口請求的,而且直接返回html代碼,所以,去看了一下Java是如何解析html里面的內(nèi)容
二、準備工作
我選用的是使用jsoup進行html的讀取和解析,需要加入如下依賴:
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.8.3</version>
</dependency>jsoup 是一款 Java 的HTML 解析器,可直接解析某個URL地址、HTML文本內(nèi)容。它提供了一套非常省力的API,可通過DOM,CSS以及類似于jquery的操作方法來取出和操作數(shù)據(jù)。它是基于MIT協(xié)議發(fā)布的。
jsoup的主要功能如下:
1、從一個URL,文件或字符串中解析HTML;
2、使用DOM或CSS選擇器來查找、取出數(shù)據(jù);
3、可操作HTML元素、屬性、文本;
示例代碼:
//獲取html的文檔對象
Document doc = Jsoup.parse("http://www.dangdang.com");
//獲取頁面下id="content"的標簽
Element content = doc.getElementById("content");
//獲取頁面下的a標簽
Elements links = content.getElementsByTag("a");
for (Element link : links) {
//獲取a標簽下的href的屬性值
String linkHref = link.attr("href");
//獲取a標簽下的文本內(nèi)容
String linkText = link.text();
}Elements這個對象提供了一系列類似于DOM的方法來查找元素,抽取并處理其中的數(shù)據(jù)。具體如下:
1、查找元素
getElementById(String id)
getElementsByTag(String tag)
getElementsByClass(String className)
getElementsByAttribute(String key) (and related methods)
Element siblings: siblingElements(), firstElementSibling(), lastElementSibling();nextElementSibling(), previousElementSibling()
Graph: parent(), children(), child(int index)
2、元素數(shù)據(jù)
attr(String key)獲取屬性
attr(String key, String value)設置屬性
attributes()獲取所有屬性
id(), className() and classNames()
text()獲取文本內(nèi)容
text(String value) 設置文本內(nèi)容
html()獲取元素內(nèi)
HTMLhtml(String value)設置元素內(nèi)的HTML內(nèi)容
outerHtml()獲取元素外HTML內(nèi)容
data()獲取數(shù)據(jù)內(nèi)容(例如:script和style標簽)
tag() and tagName()
3、操作HTML和文本
append(String html), prepend(String html)
appendText(String text), prependText(String text)
appendElement(String tagName), prependElement(String tagName) html(String value)
三、開始爬取網(wǎng)站數(shù)據(jù)
直接上代碼:
Test.java:
@Slf4j
@SpringBootTest
class Test {
@Resource
private PositionService positionService;
/**
* 爬取省市區(qū)網(wǎng)站
*/
@Test
public void test2() throws InterruptedException {
//一共五級
for (int i = 0 ; i < 5 ; i++) {
if (i == 0) {
List<PositionEntity> positionEntities = PositionUtils.reqPosition(PositionUtils.URL_HEAD);
savePosition(positionEntities, null, i);
continue;
}
List<Position> positions = positionService.findListByLevel(i);
for (Position parentPosition : positions) {
List<PositionEntity> positionEntities = PositionUtils.reqPosition(String.format("%s%s%s", PositionUtils.URL_HEAD, parentPosition.getSn(), PositionUtils.URL_TAIL));
savePosition(positionEntities, parentPosition, i);
}
}
}
/**
* 報錯地址信息
*/
private void savePosition(List<PositionEntity> positionEntities, Position parentPosition, int i){
for (PositionEntity entity : positionEntities) {
Position position = new Position();
position.setSn(entity.getCode());
position.setFullInitials(PinyinUtils.strFirst2Pinyin((parentPosition != null ? parentPosition.getFullName() : "")+entity.getName()));
position.setFullName((parentPosition != null ? parentPosition.getFullName() : "")+entity.getName());
position.setLevel(i + 1);
position.setName(entity.getName());
position.setOrderNumber(0);
position.setPsn(parentPosition != null ? parentPosition.getSn() : 0L);
long count = positionService.countBySn(position.getSn());
if (count == 0) {
positionService.savePosition(position);
}
}
}
}PositionService.java:
public interface PositionService {
void savePosition(Position position);
long countBySn(Long sn);
List<Position> findListByLevel(Integer level);
}PositionServiceImpl.java:
@Service
public class PositionServiceImpl extends ServiceImpl<PositionMapper, Position> implements PositionService {
@Override
public void savePosition(Position position) {
baseMapper.insert(position);
}
@Override
public long countBySn(Long sn) {
return baseMapper.selectCount(new QueryWrapper<Position>().lambda().eq(Position::getSn, sn));
}
@Override
public List<Position> findListByLevel(Integer level) {
return baseMapper.selectList(new QueryWrapper<Position>().lambda().eq(Position::getLevel, level));
}
}PositionMapper.java:
@Repository
public interface PositionMapper extends BaseMapper<Position> {
}Position.java:
@Data
@TableName("position")
@EqualsAndHashCode()
public class Position implements Serializable {
@TableId(type = IdType.AUTO)
private Integer id;
/**
* 編碼
*/
private Long sn;
/**
* 上級地址編碼
*/
private Long psn;
/**
* 名稱
*/
private String name;
/**
* 簡稱
*/
private String shortName;
/**
* 層級
*/
private Integer level;
/**
* 區(qū)號
*/
private String code;
/**
* 郵政編碼
*/
private String zip;
/**
* 拼音
*/
private String spell;
/**
* 拼音首字母
*/
private String spellFirst;
/**
* 地址全名
*/
private String fullName;
/**
* 地址全名拼音首字母
*/
private String fullInitials;
/**
* 排序
*/
private Integer orderNumber;
}PositionMapper.xml:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.wkf.workrecord.dao.PositionMapper"> </mapper>
PositionUtils.java:
public class PositionUtils {
public final static String URL_HEAD = "https://xingzhengquhua.bmcx.com/";
public final static String URL_TAIL = "__xingzhengquhua/";
public static List<PositionEntity> reqPosition(String url) throws InterruptedException {
String htmlStr = HttpUtils.getRequest(url);
//解析字符串為Document對象
Document doc = Jsoup.parse(htmlStr);
//獲取body元素,獲取class="fc"的table元素
Elements table = doc.body().getElementsByAttributeValue("bgcolor", "#C5D5C5");
//獲取tbody元素
Elements children;
children = table.first().children();
//獲取tr元素集合
Elements tr = children.get(0).getElementsByTag("tr");
List<PositionEntity> result = new ArrayList<>();
//遍歷tr元素,獲取td元素,并打印
for (int i = 3; i < tr.size(); i++) {
Element e1 = tr.get(i);
Elements td = e1.getElementsByTag("td");
if (td.size() < 2) {
break;
}
String name = td.get(0).getElementsByTag("td").first().getElementsByTag("a").text();
String code = td.get(1).getElementsByTag("td").first().getElementsByTag("a").text();
if (CheckUtils.isEmpty(name) || CheckUtils.isEmpty(code)) {
continue;
}
result.add(new PositionEntity(name, Long.parseLong(code)));
}
//防止ip被封
Thread.sleep(10000);
return result;
}
}PinyinUtils.java:
public class PinyinUtils {
private final static HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
static {
format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
format.setVCharType(HanyuPinyinVCharType.WITH_V);
}
/**
* 字符串轉拼音
*
* @param str
* 中文字符串
* @param fill
* 分隔符
* @return 返回中文的拼音串
*/
public static String str2Pinyin(String str, String fill) {
if (null == str) {
return null;
}
try {
StringBuilder sb = new StringBuilder();
if (fill == null)
fill = "";
boolean isCn = true;
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if (i > 0 && isCn) {
sb.append(fill);
}
if (c == ' ') {
sb.append(fill);
}
// 1、判斷c是不是中文
if (c >= '\u4e00' && c <= '\u9fa5') {
isCn = true;
String[] piyins = PinyinHelper.toHanyuPinyinStringArray(c, format);
if (null == piyins || 0 >= piyins.length) {
continue;
}
sb.append(piyins[0]);
} else {
// 不是中文
if (c >= 'A' && c <= 'Z') {
sb.append((char)(c + 32));
} else {
sb.append(c);
}
isCn = false;
}
}
return sb.toString();
} catch (BadHanyuPinyinOutputFormatCombination e) {
e.printStackTrace();
}
return null;
}
/**
* 拼音首字母
*
* @param str
* 中文字符串
* @return 中文字符串的拼音首字母
*/
public static String strFirst2Pinyin(String str) {
if (null == str) {
return null;
}
try {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
// 1、判斷c是不是中文
if (c >= '\u4e00' && c <= '\u9fa5') {
String[] piyins = PinyinHelper.toHanyuPinyinStringArray(c, format);
if (null == piyins || 0 >= piyins.length) {
continue;
}
sb.append(piyins[0].charAt(0));
} else {
// 不是中文
if (c >= 'A' && c <= 'Z') {
sb.append((char)(c + 32));
} else {
sb.append(c);
}
}
}
return sb.toString();
} catch (BadHanyuPinyinOutputFormatCombination e) {
e.printStackTrace();
}
return null;
}
}總結
到此這篇關于Java如何解析html中的內(nèi)容并存到數(shù)據(jù)庫的文章就介紹到這了,更多相關Java解析html內(nèi)容并保存內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Spring中@Configuration和@Component注解的區(qū)別及原理
這篇文章主要介紹了Spring中@Configuration和@Component注解的區(qū)別及原理,從功能上來講,這些注解所負責的功能的確不相同,但是從本質上來講,Spring內(nèi)部都將其作為配置注解進行處理,需要的朋友可以參考下2023-11-11
Java畢業(yè)設計之多用戶宿舍管理系統(tǒng)的實現(xiàn)
這篇文章主要介紹了基于Java實現(xiàn)的多用戶宿舍管理系統(tǒng),本文采用了jsp、servlet、jdbc等技術,文中示例代碼講解詳細,需要的可以參考一下2022-02-02
SpringBoot異常處理器的使用與添加員工功能實現(xiàn)流程介紹
設計完了登錄與退出功能還只完成了冰山一角,經(jīng)過測試發(fā)現(xiàn),我們以url的方式來訪問網(wǎng)站時可以直接跳過登陸頁面進入后臺頁面,這樣顯然是不合理的,下面我們通過異常攔截器+boot來做到訪問限制,以及實現(xiàn)新增員工功能,制作全局異常處理器2022-10-10

