于項目要求,需要利用php做一個獲取遠程頁面html狀態(tài)碼的功能,用來判斷遠程頁面是否可以訪問,就類似那種HTML頁面狀態(tài)碼檢測工具一樣。整理了一下代碼,貼出來。有需要的可以拿去用哦。
php獲取遠程頁面的html狀態(tài)碼,有兩種方法。一種是用了php的內(nèi)置函數(shù) get_headers(),一種是用了 curl 方法。
方法一:
<?php $arr = get_headers('http://feiniaomy.com'); echo $arr[0]; echo '<br/>'; $arr1 = get_headers('http://www.feiniaomy.com'); echo $arr1[0]; echo '<br/>'; $arr2 = get_headers('https://www.feiniaomy.com'); echo $arr2[0]; ?>
輸出結(jié)果:
HTTP/1.1 301 Moved Permanently HTTP/1.1 301 Moved Permanently HTTP/1.1 200 OK
注:
由于測試網(wǎng)址(本博客url) HTTP 協(xié)議都301到了 HTTPS 協(xié)議的 www 二級域名上,所以前兩次會輸出html的301狀態(tài)碼,最后一次是直接請求的 HTTPS 協(xié)議的地址,直接返回 HTML 200的狀態(tài)碼。
方法二:
<?php $ch = curl_init('https://www.baidu.com'); curl_setopt($ch,CURLOPT_RETURNTRANSFER,1); curl_exec($ch); $httpcode = curl_getinfo($ch,CURLINFO_HTTP_CODE); curl_close($ch); ?>
返回結(jié)果:
s 獲取頁面中的script標(biāo)簽,這是網(wǎng)上的一段代碼,經(jīng)測試,可以獲取。
var child = document.children;
var arr = [];//用來存放獲取到的所有的標(biāo)簽
function fn(obj) {
for (var i = 0; i < obj.length; i++) {
if (obj[i].children) {//當(dāng)當(dāng)前元素還存在子元素的時候,遞歸
fn(obj[i].children);
}
arr.push(obj[i]); //遍歷到的元素添加到arr這個數(shù)組中間
}
}
fn(child);
console.log(arr);//打印出最后獲取到的結(jié)果
for (var i = (arr.length - 1); i >= 0; i--) {
console.log(arr[i]);
}
其實,這段JavaScript代碼的主要目的是獲取當(dāng)前頁面中的所有DOM元素,包括script標(biāo)簽,并將它們存儲在一個數(shù)組中。然而,它實際上并沒有針對script標(biāo)簽進行特殊處理,而是獲取了document.children的所有直接子元素及其子孫元素。
如果要專門獲取頁面中的所有script標(biāo)簽,我們可以稍微修改一下這段代碼:
var arr = [];
function collectScripts(obj) {
for (var i = 0; i < obj.length; i++) {
if (obj[i].nodeName === 'SCRIPT') { // 當(dāng)前元素為script標(biāo)簽時
arr.push(obj[i]);
}
if (obj[i].children && obj[i].children.length > 0) {
collectScripts(obj[i].children); // 如果當(dāng)前元素還存在子元素,則遞歸查找
}
}
}
// 遍歷整個文檔對象模型以查找script標(biāo)簽
collectScripts(document.getElementsByTagName('html')[0].children);
console.log(arr); // 打印出所有找到的script標(biāo)簽
for (var i = (arr.length - 1); i >= 0; i--) {
console.log(arr[i]);
}
看結(jié)果:
這里,我們首先通過document.getElementsByTagName('html')[0].children來獲取HTML文檔的頂層子元素,然后在遍歷過程中檢查每個元素是否是script標(biāo)簽,如果是則將其添加到數(shù)組arr中。同時保留了原有的遞歸結(jié)構(gòu),以便能夠捕獲嵌套在其他標(biāo)簽內(nèi)的script標(biāo)簽。非常完美的實現(xiàn)了獲取頁面中的script標(biāo)簽。你也可以復(fù)制到瀏覽器的控制臺試試。非常好用!
<!--SpringMVC-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--SpringData Jpa-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--MySQL連接包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<!-- HttpClient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<!--Jsoup-->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.15.2</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
復(fù)制代碼
# MySQL配置
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=123456
# JPA配置
spring.jpa.database=MySQL
spring.jpa.show-sql=true
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.ImprovedNamingStrategy
復(fù)制代碼
@Entity
@Table(name = "item")
@Data
public class Item {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
//標(biāo)準(zhǔn)產(chǎn)品單位
private Long spu;
//庫存量單位
private Long sku;
//商品標(biāo)題
private String title;
//商品價格
private Double price;
//商品圖片
private String pic;
//商品詳情地址
private String url;
//店鋪;
private String shop;
//創(chuàng)建時間
private Date created;
//更新時間
private Date updated;
}
復(fù)制代碼
public interface ItemDao extends JpaRepository<Item,Long> {
}
復(fù)制代碼
public interface ItemService {
/**
* 保存商品
*
* @param item
*/
void save(Item item);
/**
* 刪除所有商品
*/
void deleteAll();
}
@Service
public class ItemServiceImpl implements ItemService {
@Autowired
private ItemDao itemDao;
@Override
@Transactional
public void save(Item item) {
this.itemDao.save(item);
}
@Override
public void deleteAll() {
this.itemDao.deleteAll();
}
}
復(fù)制代碼
@Component
public class HttpUtils {
private static final String FILEPATH = "D:\\demo\\";
private PoolingHttpClientConnectionManager cm;
public HttpUtils() {
this.cm = new PoolingHttpClientConnectionManager();
//設(shè)置最大連接數(shù)
this.cm.setMaxTotal(100);
//設(shè)置每個主機的最大連接數(shù)
this.cm.setDefaultMaxPerRoute(10);
}
/**
* 根據(jù)請求地址下載頁面數(shù)據(jù)
*
* @param url
* @return 頁面數(shù)據(jù)
*/
public String doGetHtml(String url) {
//獲取HttpClient對象
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(this.cm).build();
//創(chuàng)建httpGet請求對象,設(shè)置url地址
HttpGet httpGet = new HttpGet(url);
httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36");
//設(shè)置請求信息
httpGet.setConfig(this.getConfig());
CloseableHttpResponse response = null;
try {
//使用HttpClient發(fā)起請求,獲取響應(yīng)
response = httpClient.execute(httpGet);
//解析響應(yīng),返回結(jié)果
if (response.getStatusLine().getStatusCode() == 200) {
//判斷響應(yīng)體Entity是否不為空,如果不為空就可以使用EntityUtils
if (response.getEntity() != null) {
String content = EntityUtils.toString(response.getEntity(), "utf8");
return content;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//關(guān)閉response
if (response != null) {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//返回空串
return "";
}
/**
* 下載圖片
*
* @param url
* @return 圖片名稱
*/
public String doGetImage(String url) {
//獲取HttpClient對象
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(this.cm).build();
//創(chuàng)建httpGet請求對象,設(shè)置url地址
HttpGet httpGet = new HttpGet(url);
//設(shè)置請求信息
httpGet.setConfig(this.getConfig());
CloseableHttpResponse response = null;
try {
//使用HttpClient發(fā)起請求,獲取響應(yīng)
response = httpClient.execute(httpGet);
//解析響應(yīng),返回結(jié)果
if (response.getStatusLine().getStatusCode() == 200) {
//判斷響應(yīng)體Entity是否不為空
if (response.getEntity() != null) {
//獲取圖片的后綴
String extName = url.substring(url.lastIndexOf("."));
//創(chuàng)建圖片名,重命名圖片
String picName = UUID.randomUUID() + extName;
//聲明OutPutStream
OutputStream outputStream = new FileOutputStream(new File(FILEPATH + picName));
response.getEntity().writeTo(outputStream);
//返回圖片名稱
return picName;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//關(guān)閉response
if (response != null) {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//如果下載失敗,返回空串
return "";
}
/**
* 設(shè)置請求信息
*
* @return
*/
private RequestConfig getConfig() {
RequestConfig config = RequestConfig.custom()
//創(chuàng)建連接的最長時間
.setConnectTimeout(1000)
// 獲取連接的最長時間
.setConnectionRequestTimeout(500)
//數(shù)據(jù)傳輸?shù)淖铋L時間
.setSocketTimeout(10000)
.build();
return config;
}
}
復(fù)制代碼
SPU
SPU是商品信息聚合的最小單位,是一組可復(fù)用、易檢索的標(biāo)準(zhǔn)化信息的集合,該集合描述了一個產(chǎn)品的特性。
屬性值、特性相同的商品就可以稱為一個SPU。
如:某型號某配置某顏色的筆記本電腦就對應(yīng)一個SPU,它有多種配置,或者多種顏色
SKU
SKU即庫存進出計量的單位, 可以是以件、盒、托盤等為單位。SKU是物理上不可分割的最小存貨單元。在使用時要根據(jù)不同業(yè)態(tài),不同管理模式來處理。
如:某型號的筆記本電腦有多種配置,8G+512G筆記本電腦就是一個SKU。
爬取筆記本電腦搜索頁面。進行分頁操作,得到分頁請求地址:https://search.jd.com/search?keyword=%E7%94%B5%E8%84%91&wq=%E7%94%B5%E8%84%91&pvid=56a110735c6c491c91416c194aed4c5b&cid3=672&cid2=671&s=56&click=0&page=
所有商品由一個class=J_goodsList的div包裹。div中則是由ul標(biāo)簽包裹的li標(biāo)簽,每一個li標(biāo)簽對應(yīng)一個商品信息。
li標(biāo)簽包含的需要的商品信息
@Component
public class ItemTask {
@Autowired
private HttpUtils httpUtils;
@Autowired
private ItemService itemService;
/**
* 使用定時任務(wù)抓取最新數(shù)據(jù)
*
* @throws Exception
*/
@Scheduled(fixedDelay = 50 * 1000)
public void itemTask() throws Exception {
// 每次執(zhí)行前請客數(shù)據(jù)
itemService.deleteAll();
//聲明需要解析的初始地址
String url = "https://search.jd.com/search?keyword=%E7%94%B5%E8%84%91&wq=%E7%94%B5%E8%84%91&pvid=56a110735c6c491c91416c194aed4c5b&cid3=672&cid2=671&s=56&click=0&page=";
// 按照頁面對搜索結(jié)果進行遍歷解析,注意頁面是奇數(shù)
for (int i = 1; i < 10; i = i + 2) {
String html = httpUtils.doGetHtml(url + i);
// 解析頁面,獲取商品數(shù)據(jù)并存儲
this.parse(html);
}
System.out.println("商品數(shù)據(jù)抓取完成!");
}
/**
* 解析頁面,獲取商品數(shù)據(jù)并存儲
*
* @param html
* @throws Exception
*/
private void parse(String html) {
// 解析html獲取Document
Document doc = Jsoup.parse(html);
// 獲取spu信息
Elements spuEles = doc.select("div#J_goodsList > ul > li");
// 循環(huán)列表中的SPU信息
for (int i = 0; i < spuEles.size(); i++) {
Element element = spuEles.get(i);
//獲取spu
String strSpu = element.attr("data-spu");
if (strSpu == null || strSpu.equals("")) {
continue;
}
long spu = Long.parseLong(strSpu);
//獲取sku
long sku = Long.parseLong(element.attr("data-sku"));
Item item = new Item();
//設(shè)置商品的spu
item.setSpu(spu);
//設(shè)置商品的sku
item.setSku(sku);
//獲取商品的詳情的url
String itemUrl = "https://item.jd.com/" + sku + ".html";
item.setUrl(itemUrl);
// 獲取商品的圖片
String picUrl = "https:" + element.select("div.p-img").select("a").select("img").attr("data-lazy-img");
String picName = this.httpUtils.doGetImage(picUrl);
item.setPic(picName);
//獲取商品的價格
String strPrice = element.select("div.p-price").select("i").text();
item.setPrice(Double.parseDouble(strPrice));
//獲取商品的標(biāo)題
String title = element.select("div.p-name").select("a").attr("title");
item.setTitle(title);
// 店鋪名稱
String shopName = element.select("div.p-shop a").text();
item.setShop(shopName);
item.setCreated(new Date());
item.setUpdated(item.getCreated());
//保存商品數(shù)據(jù)到數(shù)據(jù)庫中
this.itemService.save(item);
}
}
}
復(fù)制代碼
@SpringBootApplication
// 開啟定時任務(wù)
@EnableScheduling
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
復(fù)制代碼
啟動項目,執(zhí)行測試。查看數(shù)據(jù)庫與本地下載照片。
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。