一、分析掘金网页

1. 获取浏览器URL

直接页面选择30内最热门的文章可得到URL地址为 https://juejin.im/timeline?sort=monthly_hottest 查看该网页Dom元素发现并没有文章的数据,可得知此为动态网页。

2. 获得数据API

在这里插入图片描述在这里插入图片描述
由此得到获取文章的API为 https://web-api.juejin.im/query

3. 分析请求参数

在登录状态下访问该接口的 Header 中自定义的参数有:

X-Agent: Juejin/Web
X-Legacy-Device-Id: 1575538149621
X-Legacy-Token: eyJhY2Nlc3NfdG9rZWS4iOiJjMHFEbFBnZ1pFMmZtN3NxIiwicmVmcmVzaF90b2tlbiI6IkVoSXJPSlhvTEhRYlRBZmgiLCJ0b2tlbl90eXBlIjoibWFjIiwiZXhwaXJlX2luIjoyNTkyMDAwfDGQ==
X-Legacy-Uid: 5b502c73f265da0f9d19fc58

POST 的参数为:

{
    "operationName":"",
    "query":"",
    "variables":{
        "tags":[

        ],
        "category":"5562b415e4b00c57d9b94ac8",
        "first":20,
        "after":"",
        "order":"MONTHLY_HOTTEST"
    },
    "extensions":{
        "query":{
            "id":"653b587c5c7c8a00ddf67fc66f989d42"
        }
    }
}

未登陆状态下的 Header 自定义参数为:

X-Agent: Juejin/Web
X-Legacy-Device-Id: 
X-Legacy-Token: 
X-Legacy-Uid:

POST 的参数为:

{
    "operationName":"",
    "query":"",
    "variables":{
        "tags":[

        ],
        "category":"5562b415e4b00c57d9b94ac8",
        "first":20,
        "after":"",
        "order":"MONTHLY_HOTTEST"
    },
    "extensions":{
        "query":{
            "id":"653b587c5c7c8a00ddf67fc66f989d42"
        }
    }
}

从上对比得出Header中 X-Legacy-Device-Id X-Legacy-Token X-Legacy-Uid 不必要,只需要 X-Agent: Juejin/Web 即可。 Post 的参数从中推出

Category: 5562b415e4b00c57d9b94ac8  // 此为前端分类的标识
Query : id // 顶部搜索条件
order : // 排序方式
First: // 第一页条数
tags: // Category下的标签
after: //推测为滚动条距离底部的距离 分页相关

返回的数据结构为:

数据结构
{
	data: {
		articleFeed: {
			items: {
				edges: [] ,
				pageInfo: {
				}
			}
			
		}
	}
}

最终取值应为 edges[index][nodes]

二、抓取数据

1. 使用scrapy创建项目,项目中 item 根据需求配置 item

## 此处我是用的项目名为project_002
class Project002Item(scrapy.Item):
    title = scrapy.Field() 
    content = scrapy.Field()
    url = scrapy.Field()
    like = scrapy.Field()
    user = scrapy.Field()
    category = scrapy.Field()
    updated_date = scrapy.Field()
    article_id = scrapy.Field()
    pass

2. 使用命令 scrapy genspider 文件名 网址生成爬虫文件。

生成后需改造爬虫文件
添加 Header 参数

headers = {
    "X-Agent": "Juejin/Web",
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36",
    "Content-Type": "application/json",
    "Host": "web-api.juejin.im",
    "Origin": "https://juejin.im"
}

重写 start_requests 方法,由于是直接爬取前100条,就在 first 直接写上100,不做分页处理。

def start_requests(self):
    url = 'https://web-api.juejin.im/query'
    query_data = {"operationName":"","query":"","variables":{"first":100,"after":"","order":"MONTHLY_HOTTEST"},"extensions":{"query":{"id":"21207e9ddb1de777adeaca7a2fb38030"}}}
    response = scrapy.Request(url, method="POST",headers=self.headers, body=json.dumps(query_data),callback=self.parse_item)
    yield response

回调方法处理

def parse_item(self, response):
    item = Project002Item()
    edges = json.loads(response.text)['data']['articleFeed']['items']['edges']
    temp = []
    for edge in edges:
        node = edge['node']
        temp.append({"title": node['title'], "url": node['originalUrl'], "like": node['likeCount'], 'content': node['content'], 'user': node['user']['username'], 'updated_date': node['updatedAt'], 'category': node['category']['name'], 'article_id': node['id']})
    item = temp
    return item

三、储存到数据库

将配置文件的 ITEM_PIPELINES 选项打开,在 pipelines 写入以下代码(数据库需要设计相应字段的表):

def __init__(self):
        db = {
            'host': '127.0.0.1',
            'port': 3306,
            'user': 'root',
            'password': 'root',
            'database': 'spiders',
            'charset': 'utf8'
        }
        self.conn = pymysql.connect(**db)
        self.cursor = self.conn.cursor()

    def process_item(self, item, spider):
        self.cursor.execute("""INSERT IGNORE INTO juejin (id,title,`url`,`like`,content,category,user,updated_date,article_id) 
        VALUES (null,%s,%s,%s,%s,%s,%s,%s,%s)""", 
                    (item['title'], 
                    item['url'], 
                    item['like'], item['content'], item['category'], item['user'], 
                    item['updated_date'],
                    item['article_id']
                    )
                )            
        self.conn.commit()

使用 scrapy crawl 爬虫名 即可存储相应数据
在这里插入图片描述

总结

掘金的爬虫相对比较简单,反爬机制几乎没有,非常适合入门练手。
完整代码

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐