搜索
房产
装修
汽车
婚嫁
健康
理财
旅游
美食
跳蚤
二手房
租房
招聘
二手车
教育
茶座
我要买房
买东西
装修家居
交友
职场
生活
网购
亲子
情感
龙城车友
找美食
谈婚论嫁
美女
兴趣
八卦
宠物
手机

分析Ajax爬取今日头条街拍美图-崔庆才思路

[复制链接]
查看: 27|回复: 0

1万

主题

1万

帖子

4万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
45261
发表于 2020-2-14 21:24 | 显示全部楼层 |阅读模式

  站点分析

首先,翻开首条,在搜索框输入关键字以后,在返回的页面中,勾选Perserve log,这玩意儿在页面发生变化的时候,不会扫除之前的交互信息.
在返回的response中,我们看不到常见的HTML代码,所以初步判定,这个网站是经过ajax静态加载的.

分析Ajax爬取今日头条街拍美图-崔庆才思路  热点新闻 pic-1581682361199

pic-1581682361199.png
切换到XHR过滤器,进一步检察.

分析Ajax爬取今日头条街拍美图-崔庆才思路  热点新闻 pic-1581682361200

pic-1581682361200.png
发现随着网页的转动,会发生类似这样的的Ajax请求出来. 细致检察内容,可以看到与网页中条目对应的title和article_url.
所以初步思绪,经过article_url字段先抓取文章条目
分析Ajax爬取今日头条街拍美图-崔庆才思路  热点新闻 pic-1581682361200

分析json数据,可以看到,这里有article_url,此外,此主要抓取的是图集形式的页面,所以要留意下这个has_gallery
然后我们再来看具体的页面
在具体页面的html中,我们发现,图片的全数链接间接在网页源代码中包含了,所以,我们间接拿到源码,正则婚配一下就行了.

分析Ajax爬取今日头条街拍美图-崔庆才思路  热点新闻 pic-1581682361200

pic-1581682361200.png
至此,页面分析完成.
开工!
  源码及碰到的题目

  代码结构

  方式界说

def get_page_index(offset, keyword): 获得搜索成果索引页面
def parse_page_index(html): 分析索引页面,严重是分析json内容,所以需要用到json.loads方式
def get_page_detail(url): 用来获得具体图片的页面,与索引页获得差不多
def parse_page_details(html, url):分析具体图集页面
def save_to_mongo(result): 将题目,url等内容保存到mongoDB数据库. 之所以操纵mongoDB数据库,由于mongoDB简单,而且是K-V方式的存储,对于字典范例很友爱
def download_image(url): 下载图片
def save_img(content): 保存图片
def main(offset): 对以上各类方式的挪用
  需要的常量
  1. MONGO_URL = 'localhost' # 数据库位置MONGO_DB = 'toutiao'    # 数据库名MONGO_TABLE = 'toutiao'# 表名GROUP_START = 1 # 循环肇端值GROUP_END = 20 # 循环竣事值KEY_WORD = '街拍' # 搜索关键字
复制代码
  关于在代码中碰到的题目

  01. 数据库毗连

第一次在python中操纵数据库,而且用的还是MongoDB. 操纵之前引入 pymongo库,数据库毗连的写法比力简单. 传入url 然后在建立的client中间接指定数据库称号便可以了.
  1. client = pymongo.MongoClient(MONGO_URL,connect=False)db = client[MONGO_DB]
复制代码
  02.本日头条的反爬虫机制

本日头条比力故意义,反爬虫机制不是间接给个400的回应,而是返回一些毛病的 无效的代码大要json. 不大白是什么道理,是请求差池,还是怎样了. 所以针对本日头条的反爬虫机制,经过尝试以后发现需要机关get的参数和请求头.
而且本日头条的请求头中,需要带上cookie信息. 否则返回的response还是有题目.
这里还要留意的就是cookie信息偶然效题目,具体多长时候,我也没搞大白,几个小时应当是有的,所以在尝试之前,cookie最好更新一下
一样的在获得详情页的时候也有这个题目存在. 而且还犯了一个被自己蠢哭的毛病. headers没有传到requests方式中去.
  1. def get_page_index(offset, keyword):    timestamp = int(time.time())    data = {        "aid": "24",        "app_name": "web_search",        "offset": offset,        "format": "json",        "keyword": keyword,        "autoload": "true",        "count": "20",        "en_qc": "1",        "cur_tab": "1",        "from": "search_tab",        # "pd": "synthesis",        "timestamp": timestamp    }    headers = {        # 这里警戒cookie生效的题目        'cookie': 'tt_webid=6791640396613223949; WEATHER_CITY=%E5%8C%97%E4%BA%AC; tt_webid=6791640396613223949; csrftoken=4a29b1b1d9ecf8b5168f1955d2110f16; s_v_web_id=k6g11cxe_fWBnSuA7_RBx3_4Mo4_9a9z_XNI0WS8B9Fja; ttcid=3fdf0861117e48ac8b18940a5704991216; tt_scid=8Z.7-06X5KIZrlZF0PA9kgiudolF2L5j9bu9g6Pdm.4zcvNjlzQ1enH8qMQkYW8w9feb; __tasessionId=ngww6x1t11581323903383',        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36'}    url = 'https://www.toutiao.com/api/search/content/?' + urlencode(data)    response = requests.get(url, headers=headers)    try:        if response.status_code == 200:            return response.text        return None    except RequestException:        print('Request failed!')        return None
复制代码
  03. json解码碰到的题目

由于python和java转移字符的区分(python经过''举行转义,''自己不需要转义),可是java需要\\来举行转义,也就是''自己还需要一个''来举行转义.
可是python的json.loads()方式和print方式在输出的时候城市对转义字符举行表白.
所以当初在parse_page_details()这个方式中 json.loads()报错,说json格式毛病找不到'"'. 可是print出来的时候,又是一个''的样子.
后来在在debug的时候,看到了实在的json字符串的样子
分析Ajax爬取今日头条街拍美图-崔庆才思路  热点新闻 pic-1581682361201

所以就需要对这个json字符串举行预处置惩罚,然后再操纵json.loads()举行解码.
eval(repr(result.group(1)).replace('\\\\', '\\'))
插一个小话题,那就是str()方式和repr()方式的区分. 首先两者都是把工具转换成字符串,而不管print方式还是str()方式挪用的都是类中的__str__ 而repr()方式挪用的是__repr__ .
简单来说,__str__方式是为了满足可读性,会对输出内容做可读性处置惩罚. 比如去掉字符串两真个引号大要自动分析''等. 可是__repr__会尽管保存原始数据格式,满足的是正确性需求. 所以这里,我们操纵repr()方式拿到原始数据,然后将\\ 更换为\
ps.\\\\ 是两个\ 转义了一下. 同理两个斜杠是一个斜杠,由于也是转义的.
然后就是eval方式是能把字符串转换成对应的典范.
  1. #字符串转换成列表 >>>a = "[[1,2], [3,4], [5,6], [7,8], [9,0]]" >>>type(a)  >>> b = eval(a) >>> print b [[1, 2], [3, 4], [5, 6], [7, 8], [9, 0]] >>> type(b) #字符串转换成字典>>> a = "{1: 'a', 2: 'b'}">>> type(a)>>> b = eval(a)>>> print b{1: 'a', 2: 'b'}>>> type(b)
复制代码
大白repr()和eval()两个方式以后,那上面的预处置惩罚代码就好大白了,先经过repr()方式获得原始字符串,然后更换,然后再给他转换成可读的字符串. 然后在用json.loads()解码.
  04. 关于response.text和response.content的区分

response.text 获得文本值
response.content 获得二进制内容
  源代码
  1. import jsonimport osimport refrom hashlib import md5from multiprocessing import Poolfrom urllib.parse import urlencodeimport pymongoimport requestsfrom bs4 import BeautifulSoupfrom requests.exceptions import RequestExceptionfrom config import *# mongodb 数据库工具# connext=False表现进程启动的时候才举行毗连client = pymongo.MongoClient(MONGO_URL,connect=False)db = client[MONGO_DB]def get_page_index(offset, keyword):    data = {        "aid": "24",        "app_name": "web_search",        "offset": offset,        "format": "json",        "keyword": keyword,        "autoload": "true",        "count": "20",        "en_qc": "1",        "cur_tab": "1",        "from": "search_tab",        # "pd": "synthesis",        # "timestamp": "1581315480994"    }    headers = {        # 这里警戒cookie生效的题目        'cookie': 'tt_webid=6791640396613223949; WEATHER_CITY=%E5%8C%97%E4%BA%AC; tt_webid=6791640396613223949; csrftoken=4a29b1b1d9ecf8b5168f1955d2110f16; s_v_web_id=k6g11cxe_fWBnSuA7_RBx3_4Mo4_9a9z_XNI0WS8B9Fja; ttcid=3fdf0861117e48ac8b18940a5704991216; tt_scid=8Z.7-06X5KIZrlZF0PA9kgiudolF2L5j9bu9g6Pdm.4zcvNjlzQ1enH8qMQkYW8w9feb; __tasessionId=ngww6x1t11581323903383',        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36'}    url = 'https://www.toutiao.com/api/search/content/?' + urlencode(data)    response = requests.get(url, headers=headers)    try:        if response.status_code == 200:            return response.text        return None    except RequestException:        print('Request failed!')        return Nonedef parse_page_index(html):    data = json.loads(html)    # json.loads()方式会格式化成果,并天生一个字典范例    # print(data)    # print(type(data))    try:        if data and 'data' in data.keys():            for item in data.get('data'):                if item.get('has_gallery'):                    yield item.get('article_url')    except TypeError:        passdef get_page_detail(url):    headers = {        'cookie': 'tt_webid=6791640396613223949; WEATHER_CITY=%E5%8C%97%E4%BA%AC; tt_webid=6791640396613223949; csrftoken=4a29b1b1d9ecf8b5168f1955d2110f16; s_v_web_id=k6g11cxe_fWBnSuA7_RBx3_4Mo4_9a9z_XNI0WS8B9Fja; ttcid=3fdf0861117e48ac8b18940a5704991216; tt_scid=8Z.7-06X5KIZrlZF0PA9kgiudolF2L5j9bu9g6Pdm.4zcvNjlzQ1enH8qMQkYW8w9feb; __tasessionId=yix51k4j41581315307695',        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36',        # ':scheme': 'https',        # 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',        # 'accept-encoding': 'gzip, deflate, br',        # 'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-US;q=0.7'    }    try:        # 他妈的被自己蠢哭...忘了写headers了,搞了一个多小时        response = requests.get(url, headers=headers)        # print(response.status_code)        if response.status_code == 200:            return response.text        return None    except RequestException:        print("请求详情页出错!")        return Nonedef parse_page_details(html, url):    soup = BeautifulSoup(html, 'xml')    title = soup.select('title')[0].get_text()    # print(title)    img_pattern = re.compile('JSON.parse\("(.*?)"\),', re.S)    result = re.search(img_pattern, html)    if result:        # 这里留意一下双斜杠的题目        data = json.loads(eval(repr(result.group(1)).replace('\\\\', '\\')))        if data and 'sub_images' in data.keys():            sub_images = data.get('sub_images')            images = [item.get('url') for item in sub_images]            for image in images: download_image(image)            return {                'title': title,                'url': url,                'images': images            }def save_to_mongo(result):    if db[MONGO_TABLE].insert_one(result):        print('存储到MongoDB乐成', result)        return True    return Falsedef download_image(url):    print('正鄙人载', url)    try:        response = requests.get(url)        if response.status_code == 200:            save_img(response.content)        return None    except RequestException:        print('请求图片出错', url)        return Nonedef save_img(content):    file_path = '{0}/img_download/{1}.{2}'.format(os.getcwd(), md5(content).hexdigest(), 'jpg')    if not os.path.exists(file_path):        with open(file_path, 'wb') as f:            f.write(content)            f.close()def main(offset):    html = get_page_index(offset, KEY_WORD)    for url in parse_page_index(html):        html = get_page_detail(url)        if html:            result = parse_page_details(html, url)            if result: save_to_mongo(result)if __name__ == '__main__':    groups = [x * 20 for x in range(GROUP_START, GROUP_END + 1)]    pool = Pool()    pool.map(main, groups)
复制代码
免责声明:假如加害了您的权益,请联系站长,我们会实时删除侵权内容,感谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Copyright © 2006-2014 全椒百姓网-全椒知名**,发布及时新鲜的全椒新闻资讯 生活信息 版权所有 法律顾问:高律师 客服电话:0791-88289918
技术支持:迪恩网络科技公司  Powered by Discuz! X3.2
快速回复 返回顶部 返回列表