Spring Boot集成WebMagic爬取京东商品信息
source link: https://blog.51cto.com/chencoding/5695631
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Spring Boot集成WebMagic爬取京东商品信息
爬取
https://search.jd.com/
上的商品信息。只爬取笔记本电脑
相关的商品信息。
访问页面并搜索笔记本电脑
,得到商品列表。
商品列表由
id=J_goodsList
的div包裹,每个商品对应一个li
标签,li
标签中clas=p-name
的元素包含了商品详情页地址
点击分页,得到请求地址:
https://search.jd.com/Search?keyword=%E7%AC%94%E8%AE%B0%E6%9C%AC%E7%94%B5%E8%84%91&wq=%E7%AC%94%E8%AE%B0%E6%9C%AC%E7%94%B5%E8%84%91&pvid=19a729aac00f4decbacb5de334b3be4e&page=3&s=56&click=0
。分页规则:分页是以奇数分页,如:page=1、page=3、page=5
商品详情页
进入商品详情页分析需要的数据。例如:商品sku、商品标题、商品图片、商品价格、商品详情地址、商品店铺等
在
class=preview
对应div下的img
元素的data-origin
属性获取图片地址。
标题的获取
规格的获取,通过该规格对应的sku,通过接口请求获取商品价格
graph LR
A[解析商品列表页] ----> B[获取商品详情页]
B ----> C(解析页面获取数据)
搭建基础环境
<!--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>
<!--WebMagic核心包-->
<dependency>
<groupId>us.codecraft</groupId>
<artifactId>webmagic-core</artifactId>
<version>0.7.5</version>
</dependency>
<dependency>
<groupId>us.codecraft</groupId>
<artifactId>webmagic-extension</artifactId>
<version>0.7.5</version>
</dependency>
<!--WebMagic对布隆过滤器的支持-->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--工具包-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
@Entity
@Table(name = "item")
@Data
public class Item {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
//商品标题
private String title;
//商品价格
private Double price;
//商品图片
private String pic;
//商品详情地址
private String url;
//规格
private String standard;
//店铺;
private String shop;
//创建时间
private Date created;
}
public interface ItemDao extends JpaRepository<Item,Long> {
}
Service
public interface ItemService {
void save(Item item);
}
@Service
public class ItemServiceImpl implements ItemService {
@Autowired
private ItemDao itemDao;
@Override
@Transactional
public void save(Item item) {
this.itemDao.save(item);
}
}
开启定时任务
在启动类开启定时任务开关,通过启动项目自动执行爬虫任务。
@SpringBootApplication
@EnableScheduling
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
HttpUtils
@Component
public class HttpUtils {
private PoolingHttpClientConnectionManager cm;
public HttpUtils() {
this.cm = new PoolingHttpClientConnectionManager();
//设置最大连接数
this.cm.setMaxTotal(100);
//设置每个主机的最大连接数
this.cm.setDefaultMaxPerRoute(10);
}
/**
* 根据请求地址下载页面数据
*
* @param url
* @return 页面数据
*/
public String doGetHtml(String url) {
//获取HttpClient对象
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(this.cm).build();
//创建httpGet请求对象,设置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");
//设置请求信息
httpGet.setConfig(this.getConfig());
CloseableHttpResponse response = null;
try {
//使用HttpClient发起请求,获取响应
response = httpClient.execute(httpGet);
//解析响应,返回结果
if (response.getStatusLine().getStatusCode() == 200) {
//判断响应体Entity是否不为空,如果不为空就可以使用EntityUtils
if (response.getEntity() != null) {
String content = EntityUtils.toString(response.getEntity(), "utf8");
return content;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭response
if (response != null) {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//返回空串
return "";
}
/**
* 设置请求信息
*
* @return
*/
private RequestConfig getConfig() {
RequestConfig config = RequestConfig.custom()
//创建连接的最长时间
.setConnectTimeout(1000)
// 获取连接的最长时间
.setConnectionRequestTimeout(500)
//数据传输的最长时间
.setSocketTimeout(10000)
.build();
return config;
}
}
自定义Pipeline保存结果
@Component
public class MyDataPipeline implements Pipeline {
@Autowired
private ItemService itemService;
@Override
public void process(ResultItems resultItems, Task task) {
//获取封装好的商品详情对象
Item itemInfo = resultItems.get("itemInfo");
if (itemInfo != null) {
this.itemService.save(itemInfo);
}
}
}
爬虫逻辑实现
@Component
public class JobProcessor implements PageProcessor {
@Autowired
private MyDataPipeline myDataPipeline;
@Autowired
private HttpUtils httpUtils;
private int currentPage=1;
private String url ="https://search.jd.com/Search?keyword=%E7%AC%94%E8%AE%B0%E6%9C%AC%E7%94%B5%E8%84%91&wq=%E7%AC%94%E8%AE%B0%E6%9C%AC%E7%94%B5%E8%84%91&pvid=19a729aac00f4decbacb5de334b3be4e&s=56&click=0&page=";
@Override
public void process(Page page) {
// 解析页面,获取商品信息详情的url地址
Selectable css = page.getHtml().css("div#J_goodsList");
List<Selectable> list = page.getHtml().css("div#J_goodsList li").nodes();
if (list.size() == 0) {
// 表示商品详情页,解析页面,获取商品详情信息,保存数据
this.saveJobInfo(page);
} else {
// 表示商品列表页,解析出详情页的url地址,放到任务队列中
for (Selectable selectable : list) {
String url = selectable.css("div.p-name").links().toString();
// 把获取到的url地址放到任务队列中
page.addTargetRequest(url);
}
// 获取下一页的url
currentPage=currentPage+2;
// 把url放到任务队列中
page.addTargetRequest(url+currentPage);
}
}
//解析页面,获取商品详情信息,保存数据
private void saveJobInfo(Page page) {
// 创建商品详情对象
Item item = new Item();
// 解析页面
Html html = page.getHtml();
// 设置详情页地址
String url = page.getUrl().toString();
item.setUrl(url);
// 商品图片地址
String src = "https://"+html.css("div.preview img", "data-origin").toString();
item.setPic(src);
// 获取名称
item.setTitle(html.css("div.sku-name","text").toString());
// 获取当前选择的sku
String sku = html.css("div#choose-attr-2 div.selected", "data-sku").toString();
// 规格
String standard = html.css("div#choose-attr-2 div.selected", "data-value").toString();
item.setStandard(standard);
// 商品价格,先获取sku,通过接口获取价格
String jsonPrice = httpUtils.doGetHtml("https://" + "p.3.cn/prices/mgets?skuIds=J_" + sku);
JSONObject priceObject = (JSONObject)JSONArray.parseArray(jsonPrice).get(0);
item.setPrice(priceObject.getDouble("p"));
// 店铺名称
String text = html.css("div.popbox-inner a", "text").toString();
// String text2 = Jsoup.parse(html.css("div.popbox-inner a").toString()).text();
item.setShop(text);
item.setCreated(new Date());
// 一个SPU商品里面包含多个SKU商品,每个SKU商品都有自己的价格,所以需要获取每个SKU商品的价格
List<Selectable> nodes = html.css("div#choose-attr-2 div.item").nodes();
for (Selectable node : nodes) {
String nodeSku = node.css("div.item","data-sku").toString();
// 排除上述处理的sku对应商品
if (sku.equals(nodeSku)){
continue;
}
page.addTargetRequest("https://item.jd.com/"+nodeSku+".html");
}
page.putField("itemInfo",item);
}
private Site site = Site.me()
.setCharset("UTF-8")//设置编码
.setTimeOut(10 * 1000)//设置超时时间
.setRetrySleepTime(3000)//设置重试的间隔时间
.setRetryTimes(3)//设置重试的次数
.addHeader("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")//设置请求头
;
@Override
public Site getSite() {
return site;
}
/**
* 定时执行
*
* initialDelay:启动项目延迟执行
* fixedDelay:每隔5s执行一次
*/
@Scheduled(initialDelay = 2000, fixedDelay = 5000)
public void process() {
Spider.create(new JobProcessor())
// 初始访问url地址
.addUrl(url+currentPage)
// 使用布隆过滤器
.setScheduler(new QueueScheduler().setDuplicateRemover(new BloomFilterDuplicateRemover(100000)))
.thread(6)
.addPipeline(this.myDataPipeline)
.run();
}
}
启动项目,执行查看结果。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK