不少网页代码里面并没有我们需要的内容,这是因为这些信息是通过 Ajax加载并渲染出来的。这时候就需要我们来分析网页的请求。
需要使用到
requests
BeautifulSoup + 正则表达式
MongoDB (Pymongo)

分析网页

我们需要抓取的链接是 : http://www.toutiao.com/search/?keyword=街拍 里面的 图集
XHR == XMLHttpRequest and Fetch
2017101915083975222624.png
这个是一个json文件,下面的网址才是实际的 URL(我们需要在代码里用到的)
我们需要从这个页面获取到json文件。
向下滑会有一个新的文件,这个文件通过 offset 递增来改变地址,每20个图集递增一次。
网页是通过 Ajax后台返回json数据然后渲染出来的。

获取 offset=0 的页面

  1. import requests
  2. from requests.exceptions import RequestException
  3. from urllib.parse import urlencode
  4. def get_page_index(offset, keyword):
  5. data = {
  6. 'offset': offset,
  7. 'format': 'json',
  8. 'keyword': keyword,
  9. 'autoload': 'true',
  10. 'count': '20',
  11. 'cur_tab': 3
  12. }
  13. url = 'http://www.toutiao.com/search_content/?' + urlencode(data)
  14. try:
  15. response = requests.get(url)
  16. if response.status_code == 200:
  17. return response.text
  18. return None
  19. except RequestException:
  20. print('请求索引页出错')
  21. return None
  22. def main():
  23. html = get_page_index(0, '街拍')
  24. print(html)
  25. if __name__ == '__main__':
  26. main()

from requests.exceptions import RequestException 错误判断
结果如下
20171019150841345950571.png

url

url 是 Requests URL 里面的地址,不是浏览器里面的地址。

data 获取

20171019150839794822472.png
需要转换为字典的形式。
由于我们需要修改 offsetkeyword 所以这两个改成变量 存储在 data 里面。

获取每个页面 offset 里面的图集 URL

需要安装 json

  1. pip3 install simplejson

完整代码

  1. import requests
  2. from requests.exceptions import RequestException
  3. from urllib.parse import urlencode
  4. import json
  5. def get_page_index(offset, keyword):
  6. data = {
  7. 'offset': offset,
  8. 'format': 'json',
  9. 'keyword': keyword,
  10. 'autoload': 'true',
  11. 'count': '20',
  12. 'cur_tab': 3
  13. }
  14. url = 'http://www.toutiao.com/search_content/?' + urlencode(data)
  15. try:
  16. response = requests.get(url)
  17. if response.status_code == 200:
  18. return response.text
  19. return None
  20. except RequestException:
  21. print('请求索引页出错')
  22. return None
  23. def parse_page_index(html):
  24. data = json.loads(html)
  25. if data and 'data' in data.keys():
  26. for item in data.get('data'):
  27. yield item.get('article_url')
  28. def main():
  29. html = get_page_index(0, '街拍')
  30. for url in parse_page_index(html):
  31. print(url)
  32. if __name__ == '__main__':
  33. main()

其中
data = json.loads(html) 把 html 变成结构化的 json 格式
if data and 'data in data.keys(): 判断 data 是在 data的 key 里面。
for item in data.get('data'): 做一个循环
yield item.get('article_url') 获取 key 为 article_url 的 数据,
yield 是一个参数(构造一个生成器)
结果如下
20171019150841355879419.png
有些 URL 不对,之后会过滤掉

获取单个页面的图片

20171025150889825072522.png
查看源代码 ,勾选 Preserve log,选择 All, 可以看到一个和网页链接是一样的文件,这个表示这个是网页原始请求
也可以直接 勾选 Font 边上的 Doc

完整代码

在获取图片链接的那一步出了问题,因为头条改了。

  1. import json
  2. import os
  3. from urllib.parse import urlencode
  4. import pymongo
  5. import requests
  6. from bs4 import BeautifulSoup
  7. from requests.exceptions import ConnectionError
  8. import re
  9. from multiprocessing import Pool
  10. from hashlib import md5
  11. from json.decoder import JSONDecodeError
  12. from config import * #导入config.py 文件
  13. client = pymongo.MongoClient(MONGO_URL, connect=False)
  14. db = client[MONGO_DB]
  15. # 链接mongodb
  16. def get_page_index(offset, keyword):
  17. data = {
  18. 'autoload': 'true',
  19. 'count': 20,
  20. 'cur_tab': 3,
  21. 'format': 'json',
  22. 'keyword': keyword,
  23. 'offset': offset,
  24. }
  25. params = urlencode(data)
  26. base = 'http://www.toutiao.com/search_content/'
  27. url = base + '?' + params
  28. try:
  29. response = requests.get(url)
  30. if response.status_code == 200:
  31. return response.text
  32. return None
  33. except ConnectionError:
  34. print('Error occurred')
  35. return None
  36. def download_image(url):
  37. print('Downloading', url)
  38. try:
  39. response = requests.get(url)
  40. if response.status_code == 200:
  41. save_image(response.content)
  42. return None
  43. except ConnectionError:
  44. return None
  45. def save_image(content):
  46. file_path = '{0}/{1}.{2}'.format(os.getcwd(), md5(content).hexdigest(), 'jpg')
  47. print(file_path)
  48. if not os.path.exists(file_path):
  49. with open(file_path, 'wb') as f:
  50. f.write(content)
  51. f.close()
  52. def parse_page_index(text):
  53. try:
  54. data = json.loads(text)
  55. if data and 'data' in data.keys():
  56. for item in data.get('data'):
  57. yield item.get('article_url')
  58. except JSONDecodeError:
  59. pass
  60. def get_page_detail(url):
  61. try:
  62. response = requests.get(url)
  63. if response.status_code == 200:
  64. return response.text
  65. return None
  66. except ConnectionError:
  67. print('Error occurred')
  68. return None
  69. def parse_page_detail(html, url):
  70. soup = BeautifulSoup(html, 'lxml')
  71. result = soup.select('title')
  72. title = result[0].get_text() if result else ''
  73. images_pattern = re.compile('var gallery = (.*?);', re.S)
  74. result = re.search(images_pattern, html)
  75. if result:
  76. data = json.loads(result.group(1))
  77. if data and 'sub_images' in data.keys():
  78. sub_images = data.get('sub_images')
  79. images = [item.get('url') for item in sub_images]
  80. for image in images: download_image(image)
  81. return {
  82. 'title': title,
  83. 'url': url,
  84. 'images': images
  85. }
  86. def save_to_mongo(result):
  87. if db[MONGO_TABLE].insert(result):
  88. print('Successfully Saved to Mongo', result)
  89. return True
  90. return False
  91. def main(offset):
  92. text = get_page_index(offset, KEYWORD)
  93. urls = parse_page_index(text)
  94. for url in urls:
  95. html = get_page_detail(url)
  96. result = parse_page_detail(html, url)
  97. if result: save_to_mongo(result)
  98. pool = Pool()
  99. groups = ([x * 20 for x in range(GROUP_START, GROUP_END + 1)])
  100. pool.map(main, groups)
  101. pool.close()
  102. pool.join()

config.py 完整代码

  1. MONGO_URL = 'localhost'
  2. MONGO_DB = 'toutiao'
  3. MONGO_TABLE = 'toutiao'
  4. GROUP_START = 1
  5. GROUP_END = 20
  6. KEYWORD='街拍'