爬取今日头条街拍美图

  1 import requests
  2 from bs4 import BeautifulSoup
  3 import json,re,os
  4 from urllib.parse import urlencode
  5 from hashlib import md5
  6 from multiprocessing.pool import Pool
  7 from requests.exceptions import RequestException
  8 import pymongo
  9 #引入模块config中所有变量
 10 from config import *
 11 from json.decoder import JSONDecodeError
 12 
 13 #声明MongoDB对象
 14 client = pymongo.MongoClient(MONGO_URL,connect=False)
 15 db = client[MONGO_DB]
 16 #这里插入到MongoDB。
 17 #保存到本地
 18 def save_to_mongo(result):
 19     if db[MONGO_TABLE].insert(result):
 20         print('存储到MongoDB成功',result)
 21         return True
 22     else:
 23         return False
 24 #获取索引页数据
 25 def get_page_index(offset,keyword):
 26     data = {
 27         'offset': offset,
 28         'format': 'json',
 29         'keyword': keyword,
 30         'autoload': 'true',
 31         'count': '20',
 32         'cur_tab': '3',
 33         'from':'gallery'
 34     }
 35     #将data变成请求参数
 36     url = 'https://www.toutiao.com/search_content/?'+urlencode(data)
 37     try:
 38         response = requests.get(url)
 39         response.raise_for_status()
 40         response.encoding = response.apparent_encoding
 41         return response.text
 42     except RequestException:
 43         print('爬取索引页失败!')
 44 #解析索引页
 45 def parse_page_index(html):
 46     # 获取所有详情页的url
 47     try:
 48         # 页面是json格式的,装换成字符串格式
 49         data = json.loads(html)
 50         # data.keys()返回所有键名
 51         if data and 'data' in data.keys():
 52             for item in data.get('data'):
 53                 if item.get('cell_type') is not None:
 54                     continue
 55                 yield item.get('article_url')
 56     except JSONDecodeError:
 57         pass
 58 
 59 def get_page_detail(url):
 60     # 请求详情页的url
 61 
 62     #这里不加 headers 是获取不到数据的。
 63     headers = {'user-agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 '}
 64     try:
 65         response = requests.get(url,headers=headers)
 66         response.raise_for_status()
 67         response.encoding = response.apparent_encoding
 68         return response.text
 69     except RequestException:
 70         print('爬取详情页失败!',url)
 71         return None
 72 
 73 def parse_page_detail(html,url):
 74     '''获取详情页的标题和图片地址url'''
 75 
 76     #利用BeautifulSoup库提取标题
 77     try:
 78         soup = BeautifulSoup(html,'lxml')
 79         title = soup.select('title')[0].get_text()
 80         if title: print(title)
 81     except:
 82         pass
 83 
 84     # 利用正则表达式提取图片地址
 85     images_pattern = re.compile(r'.*?gallery: JSON.parse\(\"(.*?)\"\)',re.S|re.M)
 86     result = re.search(images_pattern,html)
 87 
 88     if result:
 89         data = json.loads(result.group(1).replace('\\',''))
 90         #print(data)
 91         if data and 'sub_images' in data.keys():
 92             sub_images = data.get('sub_images')
 93             #提取图片
 94             images = [item.get('url') for item in sub_images]
 95             #保存图片到本地
 96             for image in images:
 97                 download_image(title,image)
 98             return {'title':title,
 99                     'url':url,
100                     'images':images}
101     else:
102         print('')
103 
104 def save_image(title,result):
105     img_path = 'image' + os.path.sep + title
106     if not os.path.exists(img_path):
107         os.makedirs(img_path)
108     file_path = '{0}/{1}.{2}'.format(img_path,md5(result).hexdigest(),'jpg')
109     if not os.path.exists(file_path):
110         with open(file_path,'wb') as f:
111             f.write(result)
112         print("%s下载完成"%file_path)
113     else:
114         print("%s已经存在"%file_path)
115 
116 def download_image(title,url):
117     try:
118         print('正在下载',url)
119         r = requests.get(url)
120         r.raise_for_status()
121         r.encoding=r.apparent_encoding
122         save_image(title,r.content)
123 
124     except RequestException:
125         print('请求图片出错',url)
126         return False
127 
128 def main(offset):
129     #调用函数
130     html= get_page_index(offset,KEYWORD)
131     for url in parse_page_index(html):
132         html = get_page_detail(url)
133         if html:
134             result = parse_page_detail(html,url)
135             # print(result)
136             if result: save_to_mongo(result)
137 
138 
139 
140 if __name__=='__main__':
141     #开启多线程抓取
142     pool = Pool()
143     group = [x*20 for x in range(GROUP_START,GROUP_END+1)]
144     pool.map(main,group)
145     pool.close()

以上是spider.py

借鉴:https://blog.csdn.net/sixkery/article/details/81836017

有两个坑我遇到的:

第一个坑
如果你点击图集,打开开发者工具,刷新一下,你会发现,你的页面在综合这一栏。
你会发现你找到的上图的参数跟我的不一样。
这里你可以刷新之后点击图集,然后向下拖动几个,让页面多加载一些。
其中「cur_tab:3」这个参数中的数字对应图集,这下你明白了吧。
到这里就好办了,我们点击 Preview


可以看到 data 下方有 article_url 当然还有 image_url ,你点击 image_url 你会发现只有四个 url ,复制链接在浏览器上打开你发现 TMD 还不是大图,还是缩略图,所以我们不用它,我们获取 article_url ,获取之后再次请求不就完了吗。


分析详情页
这里我们来看看详情页的内容

 


详情页分析
这里我们随便点开一个组图的 url 来分析,我们可以看到返回的数据是一大堆 html ,这里有必要说一下,我们在获取页面内容的时候,一般浏览器会返回给我们的是 response 里的内容。但是我们大多数爬取数据,用 xpath 、BeautifulSoup 获取数据,看的是 Elements 里的内容。这里一定要看看 response 里的内容和 Element 里的是否相同。
这里的图片地址还真是不好找,具体怎么找呢,点开图片的地址,复制下链接,在HTML里「Ctrl + F」就发现了。是在红色框里面的。

第二个坑

首先,这里获取的页面内容是 json 格式的,我们看一下这里的内容



详情页json
这里获取用BeautifulSoup 获取 title 很方便,直接去第一个 title 就好了,关键就在这个image的提取。


image.png
这里是在红色框里的,这里涉及到了正则的用法,代码里用到了反斜杠,这里是转义匹配,要不然正则会匹配不到想要的数据。
还有在源代码中出现了好多反斜杠,不去除掉还是没办法匹配。
这些坑跨过之后就一帆风顺了。

配置文件

 1 '''配置文件'''
 2 
 3 
 4 #链接地址
 5 MONGO_URL='localhost'
 6 
 7 #数据库名称
 8 MONGO_DB='toutiao'
 9 
10 #表名称
11 MONGO_TABLE='toutiao'
12 
13 GROUP_START=1
14 GROUP_END=20
15 
16 KEYWORD = '街拍'

 

另一种爬取今日头条美图

 1 import requests
 2 from urllib.parse import urlencode
 3 from requests import codes
 4 import os
 5 from hashlib import md5
 6 from multiprocessing.pool import Pool
 7 
 8 
 9 def get_page(offset):
10     params = {
11         'offset': offset,
12         'format': 'json',
13         'keyword': '街拍',
14         'autoload': 'true',
15         'count': '20',
16         'cur_tab': '1',
17         'from': 'search_tab'
18     }
19     base_url = 'https://www.toutiao.com/search_content/?'
20     url = base_url + urlencode(params)
21     try:
22         resp = requests.get(url)
23         if codes.ok == resp.status_code:
24             return resp.json()
25     except requests.ConnectionError:
26         return None
27 
28 
29 def get_images(json):
30     if json.get('data'):
31         data = json.get('data')
32         for item in data:
33             if item.get('cell_type') is not None:
34                 continue
35             title = item.get('title')
36             images = item.get('image_list')
37             for image in images:
38                 yield {
39                     'image': 'https:' + image.get('url'),
40                     'title': title
41                 }
42 
43 
44 def save_image(item):
45     img_path = 'img' + os.path.sep + item.get('title')
46     if not os.path.exists(img_path):
47         os.makedirs(img_path)
48     try:
49         resp = requests.get(item.get('image'))
50         if codes.ok == resp.status_code:
51             file_path = img_path + os.path.sep + '{file_name}.{file_suffix}'.format(
52                 file_name=md5(resp.content).hexdigest(),
53                 file_suffix='jpg')
54             if not os.path.exists(file_path):
55                 with open(file_path, 'wb') as f:
56                     f.write(resp.content)
57                 print('Downloaded image path is %s' % file_path)
58             else:
59                 print('Already Downloaded', file_path)
60     except requests.ConnectionError:
61         print('Failed to Save Image,item %s' % item)
62 
63 
64 def main(offset):
65     json = get_page(offset)
66     for item in get_images(json):
67         print(item)
68         save_image(item)
69 
70 
71 GROUP_START = 0
72 GROUP_END = 7
73 
74 if __name__ == '__main__':
75     pool = Pool()
76     groups = ([x * 20 for x in range(GROUP_START, GROUP_END + 1)])
77     pool.map(main, groups)
78     pool.close()
79     pool.join()

 

posted @ 2018-11-07 11:50  X18301096  阅读(978)  评论(0编辑  收藏  举报