今天学习分析ajax 请求,现把学得记录,
把我们在今日头条搜索街拍美图的时候,今日头条会发起ajax请求去请求图片,所以我们在网页源码中不能找到图片的url,但是今日头条网页中有一个json 文件,这相文件中就包括单个图集的具体url,通过访问这个url ,又可以获取一个json 文件,这个json 文件中有个键是url_list 这个键的值是一个List ,这个list中就存入了具体单个图片的下载url ,通过request.get()方法去访问这个url ,然后把得到content以二进制的方式写入到本地,并把其他数据存入到mongodb中,
代码如下,
1 import requests 2 import json 3 from urllib.parse import urlencode 4 from requests.exceptions import RequestException 5 import re 6 from bs4 import BeautifulSoup 7 from config import * 8 import pymongo 9 from hashlib import md5 10 import os 11 from multiprocessing import Pool 12 13 14 def get_page_index(offset, keyword): 15 """传入要数据,获得详情页面""" 16 data = { 17 "autoload": "true", 18 "count": 20, 19 "cur_tab": 3, 20 "format": "json", 21 "from": "gallery", 22 "keyword": keyword, 23 "offset": offset, 24 } 25 url = "https://www.toutiao.com/search_content/?" + urlencode(data) 26 try: 27 response = requests.get(url) 28 if response.status_code == 200: 29 return response.text 30 return None 31 except RequestException: 32 print("请求index页面失败") 33 return None 34 35 36 def parse_page_index(html): 37 """头条街拍图片存在一个json中,json中的data就是单个图集的信息""" 38 data = json.loads(html) 39 if data and "data" in data.keys(): 40 for item in data.get("data"): 41 yield item.get("article_url") 42 43 44 def get_page_detail(url): 45 """获得单个图集url的信息""" 46 try: 47 response = requests.get(url) 48 if response.status_code == 200: 49 return response.text 50 return None 51 except RequestException: 52 print("请求详情页面失败") 53 return None 54 55 56 def parse_page_detail(html, url): 57 """解析单位个图集url的网页信息,因为信息存放于一个json文件中,这个json文件嵌套在html中,所以使用re解析""" 58 # 先用BeautifulSoup获得title. 59 soup = BeautifulSoup(html, "lxml") 60 title = soup.select("title")[0].get_text() 61 print(title) 62 images_pattern = re.compile('parse("(.*?)"),', re.S) 63 result = re.search(images_pattern, html) 64 65 if result: 66 data = result.group(1) 67 data = json.loads(data) 68 if data and "url_list" in data.keys(): 69 url_list = data.get("url_list") 70 images = [item.get("url") for item in url_list] 71 for image in images: 72 # 调用函数下载图片到本地 73 download_imag(image) 74 # 返回数据,返回的数据存入mongodb 75 return { 76 "title": title, 77 "images": images, 78 "url": url, 79 } 80 81 82 def save_to_mongo(result): 83 client = pymongo.MongoClient(MONGO_URL, connect=False) 84 db = client[MONGO_DB] 85 if db[MONGO_TABLE].insert(result): 86 print("insert into mongo success", result) 87 return True 88 else: 89 return False 90 91 92 def download_imag(url): 93 """# 下载图片到本地""" 94 print("正在下载 :", url) 95 try: 96 response = requests.get(url) 97 if response.status_code == 200: 98 # response.content 返回二进制内容 99 # response.text返回字符串 100 save_image(response.content) 101 except RequestException: 102 print("请求图片失败", url) 103 104 105 def save_image(content): 106 """解析图片url的信息,把获得的信息写入本地文件中""" 107 file_path = '{0}/{1}.{2}'.format(os.getcwd(), md5(content).hexdigest(), "jpg") 108 if not os.path.exists(file_path): 109 with open(file_path, "wb") as f: 110 f.write(content) 111 112 113 def main(offset): 114 """主函数""" 115 html = get_page_index(offset, KEYWORD) 116 # 防止get_page_index没有取回数据 117 if html is not None: 118 for url in parse_page_index(html): 119 html = get_page_detail(url) 120 if html: 121 result = parse_page_detail(html, url) 122 save_to_mongo(result) 123 print(result) 124 else: 125 print("get_page_index 函数没有取到数据") 126 127 128 if __name__ == "__main__": 129 """使用多进程加快速度""" 130 groups = [x * 20 for x in range(GROUP_START, GROUP_END)] 131 pool = Pool() 132 pool.map(main, groups)
这个程度代码相对简单,逻辑清楚,程序之间参数传递及程序的跳转也不难,只是分析今日头条的ajax请求,及获得各个json文件相对复杂一点.
我把程度的配置信息写在config.py 这个文件中,这样方法之后程序的重用,
config.py 代码如下:
MONGO_URL="localhost" MONGO_DB="toutiao" MONGO_TABLE="toutiao" GROUP_START = 1 # 定义起始循环点 GROUP_END = 20 # 定义终止循环点 KEYWORD="街拍"
通过这个程序,学会了分析,这种通过发起ajax请求而获取数据的网页的源码分析.这个程序不足的地方是,这个程序是用requsts库做的解析,程序会是用函数而不是类去实现,后续要把这个程序改用scrapy 库去实现.