Spring Boot集成WebMagic爬取京东商品信息

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Boot集成WebMagic爬取京东商品信息相关的知识,希望对你有一定的参考价值。

Spring Boot集成WebMagic爬取京东商品信息

爬取分析

商品列表

访问页面并搜索笔记本电脑,得到商品列表。

商品详情页

执行流程

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>

POJO

@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;

Dao

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();
    

执行测试

启动项目,执行查看结果。

以上是关于Spring Boot集成WebMagic爬取京东商品信息的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot + WebMagic 实现网页爬虫,写得太好了!

Spring Boot + WebMagic 实现网页爬虫,写得太好了!

webmagic爬取博客园所有文章

webmagic爬取渲染网站

webmagic之爬取数据存储为TXT

学习使用Java的webmagic框架爬取网页内容