1、分析网站源码。
网站是局部动态变化,offset变化加载内容,变化范围为0,20,40···
首先要获取索引页代码数据,定义索引页,由于是ajax请求,offset根据索引变化0、20、40···,用requests获取网页代码,urlcode将字典转换为url请求参数,然后异常处理,根据url_code状态码判断请求是否成功,返回文本格式,最后定义main函数调用。可根据可变参数’offset’和’keyworld’参数调用函数,改为def get_page-index(offset,keyworld)
from urllib.parse import urlencode
import requests
from requests.exceptions import RequestException
def get_page_index():
data = {
'offset': 0,
'format': 'json',
'keyword': '街拍',
'autoload': 'true',
'count': '20',
'cur_tab': 3
}
url = 'https://www.toutiao.com/search_content/?'+urlencode(data)
try:
response = requests.get(url)
if response.status_code == 200:
return response.text
return None
except RequestException:
print("请求索引页错误")
return None
def main():
html=get_page_index()
print(html)
if __name__ == '__main__':
main()
2、解析索引页,因为html返回的是json格式的字符串对象,json.loads加载json格式文件。判断data[‘data’]键是否存在,若存在,返回’article_url’的值
def parse_page_index(html): data = json.loads(html) if data and 'data' in data.keys(): for item in data.get('data'): yield item.get('article_url')
然后在main函数中调用解析页的url
for url in parse_page_index(html): print(url)
3、获取详情页代码
ef get_page_detail(url): try: response = requests.get(url) if response.status_code == 200: return response.text return None except RequestException: print("请求详情页错误",url) return None
4、解析详情页数据。
分析详情页源代码
查看详情页的网站源代码,发现图片集的url都在gallery键的值中。
首先,获取每个图片集的标题title,用select选择title标签下的文本
def parse_page_detail(html): soup = BeautifulSoup(html,'lxml') title = soup.select('title')[0].get_text() print(title)
在main函数中判断html是否正确,返回结果
main(): html = get_page_index(0,'街拍') for url in parse_page_index(html): html = get_page_detail(url) if html: parse_page_detail(html)
获取每个图片集中的图片信息,所有图片信息都在gallery键的值中,通过re.comlile正则表达式解析,然后用search得到结果,因为此时得到的结果中信息不正确,有很多多余的反斜杠’\’,于是利用replace去掉斜杠。
获取到的结果为
image_pattern = re.compile('gallery: JSON.parse[(]"(.*?)"[)],\n',re.S) result = re.search(image_pattern,html) if result: result = result.group(1).replace('\\','') print(result)
结果是json字符串的格式,需要用loads解析,提取其中的每张照片的url,最后返回的是图集的标题、链接和每张图片的url
data=json.loads(result) if data and 'sub_images' in data.keys(): sub_images=data.get('sub_images') images_url=[item['url'] for item in sub_images] return { 'title':title, 'url':url, 'images_url':images_url }
输出的结果为:
此时,所有的信息已经提取完毕,开始存储数据
5、把数据存储到mongodb数据库中,首先在同一目录下,建立配置文件config.py,
MONGO_URL='localhost' #链接地址 MONGO_DB='toutiao' #数据库 MONGO_TABLE='toutiao' #数据集即表
通过from config import *调用该文件
#声明mongodb数据库对象 client=pymongo.MongoClient(MONGO_URL) db=client[MONGO_DB]
然后定义函数存储到数据库中,并判断如果存储成功输出相应信息
def save_to_mongo(result): if db[MONGO_TABLE].insert(result): print("存储到mongodb成功",result) return True return False
6、接下来是根据图片链接下载图片
首先定义一个函数,利用pathlib库根据传入的目录名创建一个文件目录
def create_dir(name): #根据传入的目录名创建一个目录,这里用到了 python3.4 引入的 pathlib 。 directory = Path(name) if not directory.exists(): directory.mkdir() return directory
然后定义下载图片函数,要求返回的是content,是二进制文件
def download_image(save_dir,url): print("正在下载:",url) try: response = requests.get(url) if response.status_code == 200: #调用存储图片函数,返回二进制 save_image(save_dir,response.content) return None except RequestException: print("请求图片出错",url) return None
定义存储图片函数
def save_image(save_dir,content): '''把文件保存到本地,文件有三部分内容(路径)/(文件名).(后缀) 用format构造字符串(项目路径,文件名,格式),md5文件名可以避免重复''' #os.getcwd()程序同目录 #file_path='{0}/{1}.{2}'.format(os.getcwd(),md5(content).hexdigest(),'jpg') file_path = '{0}/{1}.{2}'.format(save_dir, md5(content).hexdigest(), 'jpg') #如果文件不存在,开始存入 if not os.path.exists(file_path): with open(file_path,'wb') as f: f.write(content) f.close()
在parse_page_detail函数中,调用download_image
root_dir = create_dir('D:\spider\jiepai') # 保存图片的根目录 download_dir = create_dir(root_dir / title) # 根据每组图片的title标题名创建目录 for image in images: download_image(download_dir, image) #下载所有的图片
最后完成的效果图