实现爬虫的思路
# 准备url
-- start_url (url地址规律不明显,总数不确定,通过代码提取下一页)
xpath
找url,部分参数在当前响应中
-- url_list (页码总是明确,url地址规律)
# 发送请求,获取响应
-- 添加随机的user-agent
-- 添加随机的代理ip
-- 添加更多的headers字段,包括cookie
-- cookie可以使用session解决,self.session = requests.Session()
-- 准备一堆能用的cookie组成cookies池,存储respoonse中的cookies,下一次请求的时候,使用之前列表中的cookie来请求
-- 如果要登录,准备多个账号,使用程序获取每个账号的cookie
之后请求登录之后才能访问的网站随机的选择cookie
# 提取数据
-- 确定数据的位置
1. 数据在当前的url地址请求结果中
- 提取的是列表也数据
- 提取的是详情页的数据(确定url,发请求,提取数据)
2. 数据不再当前的url地址中
- 在network中从上往下找
- 使用chrome中的过滤条件,选择除了css,img之外的
- 使用chrome中的search all file,搜索数字和英文(如果中文搜不到)
-- 提取数据
1. xpath (可提取整块数据)
2. 正则
3. json
# 保存数据
-- 保存到本地
-- 保存到数据库
注意要点
# 尽量减少请求次数
1. 能抓列表页就不抓详情页
2. 保存获取到的html页面
# 关注网站的所有类型
1. wap页面,触屏版页面
2. H5页面
3. APP
# 多伪装
1. 动态UA
2. 代理IP
3. 不使用cookie
# 多线程,分布式
多线程实现爬虫模板
'''
思路:每个方法不同的线程,传递参数,使用队列(线程安全)
Queue.join # 让主线程阻塞,
Queue.put # 计数加一
Queue.get
Queue.task_done # 与get连用才会计数减一
'''
from queue import Queue
import requests
from lxml import etree
from threading import Thread
class QiuShiSpider(object):
def __init__(self):
self.base_url = ''
self.headers={}
self.url_list_queue = Queue()
self.parse_html_queue = Queue()
self.content_list_queue = Queue()
def get_url_list(self):
for i in range(14):
self.url_list_queue.put(self.base_url.format(i))
def parse_url(self):
while True: # 多次执行
url = self.url_list_queue.get()
res = requests.get(url,headers=self.headers)
self.parse_html_queue.put(res.content.decode())
self.url_list_queue.task_done()
def get_content_list(self):
while True:
html_str = self.parse_html_queue.get()
html = etree.HTML(html_str)
div_list = html.xpath('//div[@id="content-left"]/div')
content_list = []
for div in div_list:
# 内容在div的span标签下
item = {}
# 内容多行是个列表
item['content'] = div.xpath(".//div[@class='content']/span/text()")
item['gender'] = div.xpath(".//div[contains(@class,'articleGender')]/@class")
item['gender'] = item['gender'][0].split(' ')[-1].replace("Icon","") if len(item['gender'])>0 else None
content_list.append(item)
self.content_list_queue.put(content_list)
self.parse_html_queue.task_done()
def save_data(self):
while True:
content_list = self.content_list_queue.get()
for i in content_list:
pass
self.content_list_queue.task_done()
def run(self):
thread_list = []
# 获取url_list
get_url_list = Thread(target=self.get_url_list())
thread_list.append(get_url_list)
#发送请求,获得相应,速度慢可以起多个线程
for i in range(3):
parse_url = Thread(target=self.parse_url())
thread_list.append(parse_url)
# 提取数据
for i in range(2):
get_content_list = Thread(target=self.get_content_list())
thread_list.append(get_content_list)
# 保存
save_data = Thread(target=self.save_data())
thread_list.append(save_data)
# 所有的线程都是while true,不会结束,所以设置子线程为守护线程
for t in thread_list:
t.setDaemon(True)
t.start()
# 子线程执行完了以后,主线程再结束,join方法
for q in [self.url_list_queue,self.parse_html_queue,self.content_list_queue]:
q.join()
print('q.join()可以等队列任务完成,主线程再结束')