5

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

 1 year ago
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.
neoserver,ios ssh client

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

爬取https://search.jd.com/上的商品信息。只爬取笔记本电脑相关的商品信息。

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

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

商品列表由id=J_goodsList的div包裹,每个商品对应一个li标签,li标签中clas=p-name的元素包含了商品详情页地址

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

点击分页,得到请求地址: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、商品标题、商品图片、商品价格、商品详情地址、商品店铺等

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

class=preview对应div下的img元素的data-origin属性获取图片地址。

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

标题的获取

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

规格的获取,通过该规格对应的sku,通过接口请求获取商品价格

Spring Boot集成WebMagic爬取京东商品信息_java_06
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();
    }
}

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

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

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK