python爬虫总结,看这篇就够了
文章目录
- 爬虫课程
- urllib
- urlopen函数
- urlretrieve函数
- urlencode函数和parse_qs函数
- urlparse函数和urlsplit函数
- request.Request函数
- request.ProxyHandler函数
- 关于编码和解码
- 携带cookie登陆实例->人人网
- 保存cookie到本地
- 加载本地cookie
- requests库
- request添加代理
- 绕过ssl证书
- XPath使用
- 使用实例
- 电影天堂大型爬虫项目
- enumerate函数使用
- replace函数和strip函数
- 正则表达式
- 正则表达式匹配手机号
- 正则表达式匹配邮箱
- 正则表达式验证url
- 正则表达式验证身份证
- ^符号的含义
- 贪婪模式和非贪婪模式
- 匹配0-100的数字
- 正则表达式中group的用法
- 正则表达式中findall函数的用法
- 正则表达式中sub函数的用法
- 正则表达式中split函数的用法
- 编译正则表达式-- re.compile
- 匹配包括换行符内的所有字符re.DOTALL=re.S
- json dump和dumps函数
- json load函数和loads函数
- csv文件读写操作
- csv写入操作
- python多线程操作
- 传统代码
- 多线程使用类
- 多线程两个小的知识点
- 多线程更改全局变量锁机制
- Lock版本的生产者模式和消费者模式
- Condition锁机制
- 爬取表情包不开多线程
- 多线程下载表情包
- selenium和chromedriver驱动浏览器
- selenium和chromedriver下载使用
- selenium定位元素7种方式
- selenium操作表单元素
- select操作
- selenium行为链
- selenium操作cookies
- selenium显示等待和隐式等待
- selenium打开、切换多窗口
- selenium设置代理
- selenium webElement
- 酷狗爬取Top500(列表生成器,去除列表中空元素)
- scrapy 使用
- 1、创建scrapy项目
- Scrapy快速入门
- 安装和文档:
- 快速入门:
- 创建项目:
- 目录结构介绍:
- 使用Scrapy框架爬取糗事百科段子:
- 使用命令创建一个爬虫:
- 爬虫代码解析:
- 修改`settings.py`代码:
- 完成的爬虫代码:
- 运行scrapy项目:
- scrapy自己总结笔记
- JsonItemExporter和JsonLinesItemExporter
- CrawlSpider
- CrawlSpider爬虫:
- 创建CrawlSpider爬虫:
- LinkExtractors链接提取器:
- Rule规则类:
- 微信小程序社区CrawlSpider案例
- scrapy shell
- scrapy模拟登陆人人网
- scrapy 下载文件和图片
- 下载文件的 Files Pipeline
- 下载图片的 Image Pipeline
- map函数
- User-Agent随机请求头
- scrapy通过twisted实现异步存入数据库
爬虫课程
urllib
urlopen函数
from urllib import request
resp=request.urlopen('http://www.baidu.com')
print(resp.read())
read()读取所有数据
readline()只读取一行
readlines()逐行列表形势读取
urlretrieve函数
request.urlretrieve('http://www.xxx.com','index.html')
将网页中的内容保存到本地文件中
urlencode函数和parse_qs函数
from urllib import parse
params={'name':'xx','age':18,'greet':'hellow ddd'}
resp=parse.urlencode(params)
#将params编码
print(resp)
aa=parse.parse_qs(resp)
#将resp解码
print(aa)
搜索刘德华
from urllib import request
from urllib import parse
import ssl
#增加ssl证书验证,没有这句话报错
ssl._create_default_https_context = ssl._create_unverified_context
url='http://www.baidu.com/baidu'
params={'wd':'刘德华'}
#将汉字刘德华编码,得到整体url
qs=parse.urlencode(params)
print(qs)
url=url+'?'+qs
print(url)
resp=request.urlopen(url)
print(resp.read())
urlparse函数和urlsplit函数
url解析的两个函数
from urllib import parse
url='http://www.baidu.com/s?wd=python&uname=admin#1'
#两种获得参数的函数
result1=parse.urlparse(url)
result2=parse.urlsplit(url)
print(result1)
print(result2)#得到结果如下
ParseResult(scheme='http', netloc='www.baidu.com', path='/s', params='', query='wd=python&uname=admin', fragment='1')SplitResult(scheme='http', netloc='www.baidu.com', path='/s', query='wd=python&uname=admin', fragment='1')
request.Request函数
# encoding utf8
from urllib import request, parse
import sslssl._create_default_https_context = ssl._create_unverified_context
url = 'https://www.lagou.com/jobs/list_python?labelWords=&fromSearch=true&suginput='
#添加请求头部信息
headers = {'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) ''Chrome/83.0.4103.61 Safari/537.36 '
}
req = request.Request(url, headers=headers)
resp = request.urlopen(req)
print(resp.read().decode('utf-8'))
request.ProxyHandler函数
# encoding utf-8
from urllib import requesturl = 'http://httpbin.org/ip'
# resp=request.urlopen(url)
# print(resp.read())
#创建一个handler使用代理http或者https
handler = request.ProxyHandler({'http': '60.217.64.237:38829'})
opener = request.build_opener(handler)
resp = opener.open(url)
print(resp.read())
关于编码和解码
#write 必须写入一个str数据类型
#resp.read()读出来是一个bytes数据类型
#bytes-> decode -> str
#str-> encode -> bytes
携带cookie登陆实例->人人网
其中涉及到cookie的保存
# encoding utf-8
from urllib import request, parse
from http.cookiejar import CookieJarheaders = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:76.0) Gecko/20100101 Firefox/76.0'
}
data = {'email': '自己的手机号或者邮箱','password': '自己的密码'
}def get_opener():# 创建cookiejarcookiejar = CookieJar()# 创建handlerhandler = request.HTTPCookieProcessor(cookiejar)# 创建openeropener = request.build_opener(handler)return openerdef login_renren(opener):login_url = 'http://www.renren.com/PLogin.do'req = request.Request(login_url, headers=headers, data=parse.urlencode(data).encode('utf-8'))opener.open(req)#此处为大鹏的人人网地址
def visit_profile(opener):dapeng_url = 'http://www.renren.com/人人id/profile'resp = opener.open(dapeng_url)with open('renren.html', 'w', encoding='utf-8') as f:f.write(resp.read().decode('utf-8'))if __name__ == '__main__':opener = get_opener()login_renren(opener)visit_profile(opener)
保存cookie到本地
#encoding utf-8
from urllib import request
from http.cookiejar import MozillaCookieJar
cookiejar=MozillaCookieJar('cookie.txt')
handler=request.HTTPCookieProcessor(cookiejar)
opener=request.build_opener(handler)url='http://httpbin.org/cookies/set?course=abc'
resp=opener.open(url)
#其中的ignore_discard=True是为了让即将过期或者已经过期的cookie能够显示
cookiejar.save(ignore_discard=True)
加载本地cookie
#encoding utf-8
from urllib import request
from http.cookiejar import MozillaCookieJar
cookiejar=MozillaCookieJar('cookie.txt')
#在这里添加ignore_discard=True
cookiejar.load(ignore_discard=True)
handler=request.HTTPCookieProcessor(cookiejar)
opener=request.build_opener(handler)url='http://httpbin.org/cookies'
for cookie in cookiejar:print(cookie)
requests库
# endcoding utf-8
import requestsresp = requests.get('https://www.baidu.com/')
print(resp.content)
print(resp.url)
print(resp.status_code)
print(resp.encoding)
request添加代理
# encoding utf-8
import requestsproxy = {'http': '182.46.251.204:9999'
}
resp = requests.get('http://httpbin.org/ip', proxies=proxy)
print(resp.content)
绕过ssl证书
#添加verify=False绕过ssl证书验证
resp = requests.get('http://httpbin.org/ip', proxies=proxy,verify=False)
XPath使用
nodename 选取此节点的所有子节点
/ 从当前节点选取直接子节点
// 从当前节点选取子孙节点
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性
使用实例
获取所有的tr标签
# encoding utf-8
from lxml import etreeparser = etree.HTMLParser(encoding='utf-8')
html = etree.parse('test.html', parser=parser)
# 获取所有的tr标签
# xpath函数返回的为列表
trs = html.xpath('//tr')
for tr in trs:print(etree.tostring(tr, encoding='utf-8').decode('utf-8'))
获取第二个tr标签
tr = html.xpath('//tr[2]')
print(etree.tostring(tr, encoding='utf-8').decode('utf-8'))
获取所有类名为even的数据
trs=html.xpath('//tr[@class=even]')
print(etree.tostring(trs, encoding='utf-8').decode('utf-8'))
获取a标签的href属性
aList=html.xpath('//a/@href')
for a in aList:print(etree.tostring(a, encoding='utf-8').decode('utf-8'))
#获取tr标签下所有a的href属性fullurl=tr.xpath('.//a/@href')#获取tr标签下的文字title=tr.xpath('./td[1]//text()')
电影天堂大型爬虫项目
# encoding utf-8
import requests
from lxml import etreeBASE_DOMAIN = 'https://www.dytt8.net'
HEADERS = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36','Referer': 'https://www.dytt8.net/html/gndy/dyzz/list_23_2.html'
}def get_detail_urls(url):resp = requests.get(url, headers=HEADERS)text = resp.texthtml = etree.HTML(text)detail_urls = html.xpath("//table[@class='tbspan']//a/@href")detail_urls = map(lambda url: BASE_DOMAIN + url, detail_urls)return detail_urlsdef parse_detail_page(url):movie = {}response = requests.get(url, headers=HEADERS)text = response.content.decode('gbk')html = etree.HTML(text)title = html.xpath("//font[@color='#07519a']/text()")[0]movie['title'] = titlezoomE = html.xpath("//div[@id='Zoom']")[0]cover = zoomE.xpath(".//img/@src")[0]movie['cover'] = coverdef parse_info(info, rule):return info.replace(rule, "").strip()infos = zoomE.xpath(".//text()")# 此处enumerate函数可以返回除了基本信息以外的索引号for index, info in enumerate(infos):# print(info)# print(index)# print("=" * 10)if info.startswith('◎年 代'):info = parse_info(info, "◎年 代")movie['year'] = infoelif info.startswith('◎产 地'):info = parse_info(info, "◎产 地")movie['country'] = infoelif info.startswith('◎类 别'):info = parse_info(info, "◎类 别")movie['category'] = infoelif info.startswith('◎上映日期'):info = parse_info(info, "◎上映日期")movie['show_time'] = infoelif info.startswith('◎豆瓣评分'):info = parse_info(info, "◎豆瓣评分")movie['douban_rating'] = infoelif info.startswith('◎片 长'):info = parse_info(info, "◎片 长")movie['duration'] = infoelif info.startswith('◎导 演'):info = parse_info(info, "◎导 演")movie['director'] = infoelif info.startswith('◎主 演'):info = parse_info(info, '◎主 演')actors = [info]# 此时在主演以下所有的演员列出来,写一个条件语句,将在为符号处切割来for x in range(index + 1, len(infos)):actor = infos[x].strip()if actor.startswith('◎'):breakactors.append(actor)elif info.startswith('◎简 介'):info = parse_info(info, '◎简 介')for x in range(index + 1, len(infos)):profile = infos[x].strip()if profile.startswith('【下载地址】'):breakmovie['profile'] = profiledownload_url = html.xpath("//td[@bgcolor='#fdfddf']//a/@href")[0]movie['download_url'] = download_urlreturn moviedef spider():base_url = 'https://www.dytt8.net/html/gndy/dyzz/list_23_{}.html'movies = []for x in range(1, 8):url = base_url.format(x)detail_urls = get_detail_urls(url)for detail_url in detail_urls:movie = parse_detail_page(detail_url)movies.append(movie)print(movies)if __name__ == '__main__':spider()
enumerate函数使用
#此处enumerate函数可以返回除了基本信息以外的索引号
for index, info in enumerate(infos):print(info)print(index)
replace函数和strip函数
def parse_info(info, rule):return info.replace(rule, "").strip()
上述代码中为将rule替换为空字符串,然后将字符串两侧的空格去除
正则表达式
\w 匹配字母数字及下划线
\W 匹配非字母数字及下划线
\s 匹配任意空白字符,等价于 [\t\n\r\f].
\S 匹配任意非空字符
\d 匹配任意数字,等价于 [0-9].
\D 匹配任意非数字
[...] 用来表示一组字符,单独列出:[amk] 匹配 'a','m'或'k'
[^...] 不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符。
# 点匹配任意字符
import re
text='hellow'
ret=re.match('.',text)
print(ret.group())
正则表达式匹配手机号
import re
#验证手机号码
text='15888888888'
#$符号代表末尾
ret=re.match('1[3578]\d{9}$',text)
print(ret.group())
正则表达式匹配邮箱
import re
#验证邮箱
text='sean_123@qq.com'
ret=re.match('\w+@[a-z]+\.[a-z]+',text)
print(ret.group())
正则表达式验证url
import re
text='https://www.runoob.com/python/python-reg-expressions.html'
ret=re.match('(http|https|ftp)://[^\s]+',text)
print(ret.group())
正则表达式验证身份证
import re
text='370982199909246666'
ret=re.match('\d{17}[\dxX]',text)
print(ret.group())
^符号的含义
在函数中^表示以。。。开始,在中括号中表示取反
贪婪模式和非贪婪模式
import re
#贪婪模式
text='<h1>标题</h1>'
ret=re.match('<.+>',text)
print(ret.group())#打印结果为:<h1>标题</h1>
import re
#非贪婪模式
text='<h1>标题</h1>'
#在匹配过程中最后添加?号
ret=re.match('<.+?>',text)
print(ret.group())#打印结果为:<h1>
匹配0-100的数字
import re
#匹配0-100之间的数字
text='100'
#其中第一位为1-9,第二位有或者没有,所以加'?',但是如果是多位的话还可以匹配,所以加$,两位数字以后结尾或者是结果为100时可以匹配
ret=re.match('[1-9]\d?$|100$',text)
print(ret.group())
正则表达式中group的用法
import re
text='The price of apples is $99 and the price of oranges is $10'
ret=re.match('.*(\$\d+).*(\$\d+)',text)
#打印正则表达式匹配的所有字符
print(ret.group())
#打印正则表达式中第一个圆括号中匹配的字符
print(ret.group(1))
#打印正则表达式中第二个圆括号中匹配的字符
print(ret.group(2))
#打印正则表达式中所有子分组中匹配的字符
print(ret.groups())
#打印正则表达式中第一个圆括号和第二个圆括号中匹配的字符
print(ret.group(1,2))
#运行结果
The price of apples is $99 and the price of oranges is $10
$99
$10
('$99', '$10')
('$99', '$10')
正则表达式中findall函数的用法
找出所有满足条件的,返回的为列表
import re
text='The price of apples is $99 and the price of oranges is $10'
ret=re.findall('\$\d+',text)
print(ret)
#打印结果为
['$99', '$10']
正则表达式中sub函数的用法
sub('正则表达式','你想替换成什么字符串','替换的文本','替换的个数')
将你匹配出的字符串替换成你想要的字符串
import re
text='The price of apples is $99 and the price of oranges is $10'
ret=re.sub('\$\d+',"0",text)
print(ret)
正则表达式中split函数的用法
import re
text='hellow wor adv'
ret=re.split(' ',text)
print(ret)
编译正则表达式-- re.compile
对于经常用到的正则表达式可以先用compile函数编译,后期直接调用,提高性能
import re
#添加了flag参数 re.VERBOSE后,可以在complie中添加注释
r = re.compile(r"""
\d+ #小数点前面
\.? #小数点本身
\d* #小数点后面
""",re.VERBOSE)
text = 'the number is 20.30'
ret = re.findall(r, text)
print(ret)
匹配包括换行符内的所有字符re.DOTALL=re.S
Python的正则表达式模块re,有一个re.DOTALL的参数。默认情况下,正则表达式中的dot(.),表示所有除了换行的字符,加上re.DOTALL参数后,就是真正的所有字符了,包括换行符(\n)
json dump和dumps函数
dumps输出为json格式的字符串
dump输出为json文本,与文件操作并行
# encoding utf-8
import json
data = {'username':'李华','sex':'male','age':16}
# in_json = json.dumps(data)
# print(in_json)
#ensure_ascii=False 这个鱼文件操作中encoding='utf-8'合并使用,可以解析中文
with open('abc.json','w',encoding='utf-8') as f:json.dump(data,f,ensure_ascii=False)
json load函数和loads函数
json.loads()解码python json格式
json.load()加载python json格式文件
import jsonjson_str = '{"username": "李华", "sex": "male", "age": 16}'# 将json格式的字符串转为python数据类型的对象
jsonData = json.loads(json_str)
print(jsonData)
print(type(jsonData))# 加载json文件
with open('abc.json', 'r',encoding='utf-8') as f:data = json.load(f)print(data)# 字典类型print(type(data))
csv文件读写操作
csv.reader()用法
#reader为一个迭代器,可以遍历,x为列表形式
reader=csv.reader(f)
#next函数可以遍历第一行,如此在for循环中从index=1开始
next(reader)
for x in reader:print(x)print(x[1])
csv.DictReader()用法:
reader=csv.DictReader(f)
for x in reader:print(x)print(x[1])
csv写入操作
# encoding utf-8
import csv#当文件为元组类型时,用demo1方法写入
def csv_write_demo1():headers = ['username', 'age', 'height']values = [('张三', '18', 175),('阿娇回家', '19', 175),('张以', '18', 175),]# newline为去除换行,如果没有newline参数,则回添加'\n'with open('csv_test.csv', 'w', encoding='utf-8', newline='') as f:writer = csv.writer(f)writer.writerow(headers)writer.writerows(values)#当文件为字典类型时。用demo2方法
def csv_write_demo2():headers = ['username', 'age', 'height']values = [{'username': '张三', 'age': 18, 'height': 190},{'username': '张d', 'age': 14, 'height': 190},{'username': '张dg', 'age': 18, 'height': 190},{'username': '张大概', 'age': 18, 'height': 190},]with open('111.csv','w',encoding='utf-8',newline='') as f:writer=csv.DictWriter(f,headers)#此处为写入头部信息,虽然上面传入了headers数据,但是需要以下代码写入writer.writeheader()writer.writerows(values)if __name__ == '__main__':csv_write_demo2()
python多线程操作
传统代码
import timedef coding():for x in range(3):print('正在写代码')time.sleep(1)def drawing():for x in range(3):print('正在画图中')time.sleep(1)
def main():coding()drawing()if __name__ == '__main__':main()#打印结果
#正在写代码
#正在写代码
#正在写代码
#正在画图中
#正在画图中
#正在画图中
#共耗时6s中
开启多线程使用threading,代码如下
#encoding utf-8import threading
import timedef coding():for x in range(3):print('正在写代码')time.sleep(1)def drawing():for x in range(3):print('正在画图中')time.sleep(1)
def main():tr1=threading.Thread(target=coding)tr2=threading.Thread(target=drawing)tr1.start()tr2.start()if __name__ == '__main__':main()
#打印结果如下
#正在写代码
#正在画图中
#正在写代码
#正在画图中
#正在画图中
#正在写代码
#共耗时3s
通过以上比较,多线程提高效率,一倍多
多线程使用类
#encoding utf-8import threading
import time
class CodingThread(threading.Thread):def run(self):for x in range(3):print('正在写代码')time.sleep(1)class DrawingThread(threading.Thread):def run(self):for x in range(3):print('正在画图中')time.sleep(1)def main():tr1 = CodingThread()tr2 = DrawingThread()tr1.start()tr2.start()if __name__ == '__main__':main()
多线程两个小的知识点
#当前线程的名称
threading.current_thread()
#查看当前线程总数
threading.enumerate()
多线程更改全局变量锁机制
变量锁只有在更改全局变量时启用,访问时无需添加线程机制锁
# encoding utf-8
import threadingVALUE = 0
#建立全局锁
gLock = threading.Lock()def add_value():global VALUE#开启锁gLock.acquire()for x in range(1000000):VALUE += 1#释放锁gLock.release()print(VALUE)def main():for x in range(2):tr = threading.Thread(target=add_value)tr.start()if __name__ == '__main__':main()
Lock版本的生产者模式和消费者模式
知识点
1、想要在函数中使用全局变量,需要添加global属性
2、Consumer(name=‘生产者线程%d’ % x)name为添加线程名称
# encoding utf-8import threading
import random
import timegMoney = 1000
gLock = threading.Lock()
gTotalTimes = 10
gTimes = 0class Producter(threading.Thread):def run(self):#想要在函数中使用全局变量,需要添加global属性global gMoneyglobal gTimeswhile True:money = random.randint(100, 1000)gLock.acquire()if gTimes >= 10:#如果在此处不添加释放锁,则会导致锁无法释放,卡死在这gLock.release()breakgMoney += moneygTimes += 1print('%s生产了%d元钱,剩余%d元钱' % (threading.current_thread(), money, gMoney))gLock.release()time.sleep(0.5)class Consumer(threading.Thread):def run(self):global gMoneywhile True:money = random.randint(100, 1000)gLock.acquire()if gMoney >= money:gMoney -= moneyprint('%s消费了%d元钱,剩余%d元钱' % (threading.current_thread(), money, gMoney))else:if gTimes >= gTotalTimes:gLock.release()breakprint('%s消费%d元钱,剩余%d元钱,不足!' % (threading.current_thread(), money, gMoney))gLock.release()time.sleep(0.5)def main():for x in range(3):#添加name属性,添加线程名称tr = Consumer(name='生产者线程%d' % x)tr.start()for x in range(5):tr = Producter(name='消费者线程%d' % x)tr.start()if __name__ == '__main__':main()
Condition锁机制
# encoding utf-8import threading
import random
import timegMoney = 1000
gCondition = threading.Condition()
gTotalTimes = 10
gTimes = 0class Producter(threading.Thread):def run(self):global gMoneyglobal gTimeswhile True:money = random.randint(100, 1000)gCondition.acquire()if gTimes >= 10:gCondition.release()breakgMoney += moneygTimes += 1print('%s生产了%d元钱,剩余%d元钱' % (threading.current_thread(), money, gMoney))gCondition.notify_all()gCondition.release()time.sleep(0.5)class Consumer(threading.Thread):def run(self):global gMoneywhile True:money = random.randint(100, 1000)gCondition.acquire()while gMoney < money:if gTimes >= gTotalTimes:gCondition.release()# 此处不可放break,如果放break的话只能退出当前if循环,但是如果是return的话,则会返回整个函数returnprint('%s消费%d元钱,剩余%d元钱,不足!' % (threading.current_thread(), money, gMoney))gCondition.wait()gMoney -= moneyprint('%s消费了%d元钱,剩余%d元钱' % (threading.current_thread(), money, gMoney))gCondition.release()time.sleep(0.5)def main():for x in range(3):tr = Consumer(name='生产者线程%d' % x)tr.start()for x in range(5):tr = Producter(name='消费者线程%d' % x)tr.start()if __name__ == '__main__':main()
爬取表情包不开多线程
# encoding utf-8
import requests
from lxml import etree
from urllib import request
import os
import redef parse_page(url):headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36'}resp = requests.get(url, headers=headers)text = resp.texthtml = etree.HTML(text)imgs = html.xpath("//div[@class='page-content text-center']//img[@class!='gif']")for img in imgs:img_url = img.get("data-original")# get可以获取其中的元素alt = img.get('alt')alt = re.sub(r'[!!。\.?\?]', '', alt)# 将文件名与后缀名分割开来,0为urlsuffix = os.path.splitext(img_url)[1]filename = alt + suffix# 将文件下载到本地request.urlretrieve(img_url, 'images/' + filename)def main():for x in range(1, 101):url = 'https://www.doutula.com/photo/list/?page=%d' % xparse_page(url)breakif __name__ == '__main__':main()
Queue线程安全队列
在线程中,访问一些全局变量,加锁是一个经常的过程。如果你是想把一些数据存储到某个队列中,那么 Python内置了一个线程安全的模块叫做 queue 模块。 Python中的queue模块中提供了同步的、线程安全以列类,包括FIFO(先进出)队列 Queue,LIFO(后入先出)队列 LifoQueue。这些队列都实现了锁原语(可以理解为原子操作,即么不做,更么都做完),能够在多程中直接使
用。可以使用队列来实现线程间的同步。相关的的数如下:
1.初始化 Queue( maxsize) 创建一个先进先出的队列。
- gsize():返回队列的大小。
- empty():判断队列是否为空。
- full():判断队列是否满了。
- get():从队列中取最后一个数据
- put():将一个数据放到队列中。
block参数如果为True,队列满了以后进入阻塞状态
多线程下载表情包
# encoding utf-8
import requests
from lxml import etree
from urllib import request
import os
import re
from queue import Queue
import threadingclass Producter(threading.Thread):headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36'}def __init__(self, page_queue, img_queue, *args, **kwargs):super(Producter, self).__init__(*args, **kwargs)self.page_queue = page_queueself.img_queue = img_queuedef run(self):while True:if self.page_queue.empty():breakurl = self.page_queue.get()self.parse_page(url)def parse_page(self, url):resp = requests.get(url, headers=self.headers)text = resp.texthtml = etree.HTML(text)imgs = html.xpath("//div[@class='page-content text-center']//img[@class!='gif']")for img in imgs:img_url = img.get("data-original")# get可以获取其中的元素alt = img.get('alt')alt = re.sub(r'[!!。\.?\?\*]', '', alt)# 将文件名与后缀名分割开来,0为urlsuffix = os.path.splitext(img_url)[1]filename = alt + suffixself.img_queue.put((img_url, filename))class Consumer(threading.Thread):def __init__(self, page_queue, img_queue, *args, **kwargs):super(Consumer, self).__init__(*args, **kwargs)self.page_queue = page_queueself.img_queue = img_queuedef run(self):while True:if self.img_queue.empty() and self.page_queue.empty():breakimg_url, filename = self.img_queue.get()# 将文件下载到本地request.urlretrieve(img_url, 'images/' + filename)print(filename + " 下载完成!")def main():page_queue = Queue(100)img_queue = Queue(1000)for x in range(1, 101):url = 'https://www.doutula.com/photo/list/?page=%d' % xpage_queue.put(url)for x in range(5):t = Producter(page_queue, img_queue)t.start()for x in range(5):t = Consumer(page_queue, img_queue)t.start()if __name__ == '__main__':main()
selenium和chromedriver驱动浏览器
selenium和chromedriver下载使用
下载地址:http://chromedriver.storage.googleapis.com/index.html
chromedriver测试
# encoding utf-8
from selenium import webdriverdriver_path = '/Users/xxx/Downloads/chromedriver'
#选择谷歌浏览器
driver = webdriver.Chrome(executable_path=driver_path)
#打开百度
driver.get('https:www.baidu.com')
#打印源代码
print(driver.page_source)
#关闭当前页面
driver.close()
#关闭整个页面
driver.quit()
selenium定位元素7种方式
1、find_element_by_id()
2、find_element_by_name()
3、find_element_by_class_name()
4、find_element_by_xpath()
5、find_element_by_css_selector()
6、find_element_by_tag_name()
7、find_element_by_link_text()
此种方法是专门用来定位文本链接的,比如百度首页右上角有“新闻”,“hao123”,“地图”等链接
# encoding utf-8
from selenium import webdriver
from lxml import etreedriver_path = '/Users/xxx/Downloads/chromedriver'
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https:www.baidu.com')
# 1、如果仅仅是解析网页,运用selenium+lxml方式最为快速
html = etree.HTML(driver.page_source)
# html.xpath("")
inputTag = driver.find_element_by_id('kw')
# 如果需要在输入框中赋值,需要使用send_keys
inputTag.send_keys('python')
selenium操作表单元素
# encoding utf-8
from selenium import webdriverdriver_path = '/Users/xxx/Downloads/chromedriver'
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https:www.baidu.com')inputTag = driver.find_element_by_id('kw')
# 如果需要在输入框中赋值,需要使用send_keys
inputTag.send_keys('python')
submitTag = driver.find_element_by_id('su')
# 清除输入框的内容
inputTag.clear()
# 点击百度一下按钮
submitTag.click()
select操作
from selenium import webdriver
from selenium.webdriver.support.select import Select
import timedriver_path = '/Users/xxx/Downloads/chromedriver'
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.sina.com.cn/')
#选择选择按钮
selectButton = Select(driver.find_element_by_id('slt_01'))
#通过索引点击
selectButton.select_by_index(1)
#通过地址进入
selectButton.select_by_value('地址')
#通过文字进入
selectButton.select_by_visible_text('新闻')time.sleep(4)
driver.close()
selenium行为链
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChainsdriver_path = '/xxx/xxx/chromedriver'
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.baidu.com/')inputTag = driver.find_element_by_id('kw')
submitBtn = driver.find_element_by_id('su')
#创建行为链
actions = ActionChains(driver)
#将鼠标移动到input输入框中
actions.move_to_element(inputTag)
#将input输入框输入python
actions.send_keys_to_element(inputTag, 'python')
#将鼠标移动到submi按钮上
actions.move_to_element(submitBtn)
#点击按钮
actions.click(submitBtn)
#提交行为链
actions.perform()
selenium操作cookies
#cookies
from selenium import webdriverdriver_path = '/xxx/chromedriver'
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.baidu.com/')#打印所有的cookie
for cookie in driver.get_cookies():print(cookie)print("=="*30)
#传递key,打印出key为PSTM的cookie信息
print(driver.get_cookie('PSTM'))
#删除key为PSTM的cookie
driver.delete_cookie('PSTM')
print(driver.get_cookie('PSTM'))
#删除所有的cookie
driver.delete_all_cookies()
selenium显示等待和隐式等待
1、selenium的显示等待
原理:显示等待,就是明确的要等到某个元素的出现或者是某个元素的可点击等条件,等不到,就一直等,除非在规定的时间之内都没找到,那么久跳出Exception
2、selenium的隐式等待
原理:隐式等待,就是在创建driver时,为浏览器对象创建一个等待时间,这个方法是得不到某个元素就等待一段时间,直到拿到某个元素位置。
# encoding utf-8
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ECdriver_path = '/xxx/chromedriver'
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('http://www.baidu.com')
# 隐式等待
# driver.implicity_wait(10)
# 显示等待
element = WebDriverWait(driver, 10).util(EC.presence_of_element_located((By.ID, 'kw'))
)
element.send_keys('hello')
driver.quit()
selenium打开、切换多窗口
# encoding utf-8
from selenium import webdriverdriver_path = '/xxx/chromedriver'
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('http://www.baidu.com')
# 打开新的标签页
driver.execute_script('window.open("https://www.douban.com")')
# driver切换到第二个标签页,window_handles代表句柄
driver.switch_to.window(driver.window_handles[1])
print(driver.current_url)
selenium设置代理
# encoding utf-8
from selenium import webdriverdriver_path = '/xxx/chromedriver'
# 设置代理
options = webdriver.ChromeOptions()
options.add_argument("--proxy-server=http://113.195.18.100:9999")
driver = webdriver.Chrome(executable_path=driver_path, options=options)
driver.get('http://www.baidu.com')
selenium webElement
# encoding utf-8
from selenium import webdriverdriver_path = '/xxx/chromedriver'
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https:www.baidu.com')submitBtn=driver.find_element_by_id('su')
#driver继承自webelement,这个标签的某个属性
print(submitBtn.get_attribute("value"))
#保存截图
driver.save_screenshot('baidu.png')
酷狗爬取Top500(列表生成器,去除列表中空元素)
# encoding utf-8
import requests
from lxml import etree
from lxml.html import fromstring, tostringdef request_top_url():url = 'https://www.kugou.com/yy/rank/home/1-8888.html?from=rank'headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:77.0) Gecko/20100101 Firefox/77.0'}resp = requests.get(url, headers=headers)text = resp.textparse_detail_page(text)def parse_detail_page(text):html = etree.HTML(text)ranks = html.xpath("//span[@class='pc_temp_num']//text()")titles = html.xpath("//div[@class='pc_temp_songlist ']/ul/li")times = html.xpath("//span[@class='pc_temp_time']/text()")links=html.xpath("//a[@class='pc_temp_songname']/@href")titles1=[]for title in titles:title=title.get('title')titles1.append(title)# print(titles1)#列表生成器ranks = [r.strip() for r in ranks if len(r.strip())]times = [r.strip() for r in times]# for title in titles:# print(title)# print(titles)#解压缩for rank, title, time,link in zip(ranks, titles1, times,links):song={'rank':rank,'title':title,'time':time,'link':link}print(song)# print()def main():request_top_url()if __name__ == '__main__':main()
scrapy 使用
1、创建scrapy项目
1、创建项目
scrapy startproject [项目名称]
2、创建爬虫
scrapy genspider [爬虫名字] [爬虫域名]
注意爬虫名字跟项目名称不能冲突
spiders:存放你Spider文件,也就是你爬取的py文件
items.py:相当于一个容器,储存爬取下来的数据类型。和字典较像
middlewares.py:定义Downloader Middlewares(下载器中间件)和Spider Middlewares(蜘蛛中间件)的实现
pipelines.py:定义Item Pipeline的实现,实现数据的清洗,储存,验证。
settings.py:全局配置(请求头、多久发送一次请求、代理池)
scrapy.cfg:配置文件
Scrapy快速入门
安装和文档:
- 安装:通过
pip install scrapy
即可安装。 - Scrapy官方文档:http://doc.scrapy.org/en/latest
- Scrapy中文文档:http://scrapy-chs.readthedocs.io/zh_CN/latest/index.html
注意:
- 在
ubuntu
上安装scrapy
之前,需要先安装以下依赖:
sudo apt-get install python3-dev build-essential python3-pip libxml2-dev libxslt1-dev zlib1g-dev libffi-dev libssl-dev
,然后再通过pip install scrapy
安装。- 如果在
windows
系统下,提示这个错误ModuleNotFoundError: No module named 'win32api'
,那么使用以下命令可以解决:pip install pypiwin32
。
快速入门:
创建项目:
要使用Scrapy
框架创建项目,需要通过命令来创建。首先进入到你想把这个项目存放的目录。然后使用以下命令创建:
scrapy startproject [项目名称]
目录结构介绍:
以下介绍下主要文件的作用:
- items.py:用来存放爬虫爬取下来数据的模型。
- middlewares.py:用来存放各种中间件的文件。
- pipelines.py:用来将
items
的模型存储到本地磁盘中。 - settings.py:本爬虫的一些配置信息(比如请求头、多久发送一次请求、ip代理池等)。
- scrapy.cfg:项目的配置文件。
- spiders包:以后所有的爬虫,都是存放到这个里面。
使用Scrapy框架爬取糗事百科段子:
使用命令创建一个爬虫:
scrapy gensipder qsbk "qiushibaike.com"
创建了一个名字叫做qsbk
的爬虫,并且能爬取的网页只会限制在qiushibaike.com
这个域名下。
爬虫代码解析:
import scrapyclass QsbkSpider(scrapy.Spider):name = 'qsbk'allowed_domains = ['qiushibaike.com']start_urls = ['http://qiushibaike.com/']def parse(self, response):pass
其实这些代码我们完全可以自己手动去写,而不用命令。只不过是不用命令,自己写这些代码比较麻烦。
要创建一个Spider,那么必须自定义一个类,继承自scrapy.Spider
,然后在这个类中定义三个属性和一个方法。
- name:这个爬虫的名字,名字必须是唯一的。
- allow_domains:允许的域名。爬虫只会爬取这个域名下的网页,其他不是这个域名下的网页会被自动忽略。
- start_urls:爬虫从这个变量中的url开始。
- parse:引擎会把下载器下载回来的数据扔给爬虫解析,爬虫再把数据传给这个
parse
方法。这个是个固定的写法。这个方法的作用有两个,第一个是提取想要的数据。第二个是生成下一个请求的url。
修改settings.py
代码:
在做一个爬虫之前,一定要记得修改setttings.py
中的设置。两个地方是强烈建议设置的。
ROBOTSTXT_OBEY
设置为False。默认是True。即遵守机器协议,那么在爬虫的时候,scrapy首先去找robots.txt文件,如果没有找到。则直接停止爬取。DEFAULT_REQUEST_HEADERS
添加User-Agent
。这个也是告诉服务器,我这个请求是一个正常的请求,不是一个爬虫。
完成的爬虫代码:
-
爬虫部分代码:
import scrapyfrom abcspider.items import QsbkItemclass QsbkSpider(scrapy.Spider):name = 'qsbk'allowed_domains = ['qiushibaike.com']start_urls = ['https://www.qiushibaike.com/text/']def parse(self, response):outerbox = response.xpath("//div[@id='content-left']/div")items = []for box in outerbox:author = box.xpath(".//div[contains(@class,'author')]//h2/text()").extract_first().strip()content = box.xpath(".//div[@class='content']/span/text()").extract_first().strip()item = QsbkItem()item["author"] = authoritem["content"] = contentitems.append(item)return items
-
items.py部分代码:
import scrapyclass QsbkItem(scrapy.Item):author = scrapy.Field()content = scrapy.Field()
-
pipeline部分代码:
import jsonclass AbcspiderPipeline(object):def __init__(self):self.items = []def process_item(self, item, spider):self.items.append(dict(item))print("="*40)return itemdef close_spider(self,spider):with open('qsbk.json','w',encoding='utf-8') as fp:json.dump(self.items,fp,ensure_ascii=False)
运行scrapy项目:
运行scrapy项目。需要在终端,进入项目所在的路径,然后scrapy crawl [爬虫名字]
即可运行指定的爬虫。如果不想每次都在命令行中运行,那么可以把这个命令写在一个文件中。以后就在pycharm中执行运行这个文件就可以了。比如现在新创建一个文件叫做start.py
,然后在这个文件中填入以下代码:
from scrapy import cmdlinecmdline.execute("scrapy crawl qsbk".split())
scrapy自己总结笔记
1、response是一个scrapy.http.response.html.HtmlResponse
对象,可执行xpath
语法和css
语法来执行查询。
2、提取出来的数据是一个Selector
或者是一个SelectorList
对象,如果想要获取字符串,应该执行get()
或者getall()
方法。
3、getall()
方法获取Selector
中所有的文本,返回的是一个列表。
4、get()
方法获取Selector
中第一个文本,返回的是一个str类型。
5、如果数据解析回来,要传给pipline处理,那么可以用yield
来返回。或者是收集所有的item。最后统一return返回。
6、item:建议在items.py
中定义好魔性,以后就不用再使用自店。
7、pipline:这个是专门用来保存数据的。其中三个方法是会经常用的
-
open_spider(self, spider)
:当爬虫打开的时候执行 -
process_item(self, item, spider)
:当爬虫有item传过来的时候会被调用。 -
close_spider(self, spider)
:当爬虫关闭的时候会被调用。要激活pipline,应该在
setting.py
中,设置ITEM_PIPELINES
。示例如下:ITEM_PIPELINES = {'qsbk.pipelines.QsbkPipeline': 300, }
JsonItemExporter和JsonLinesItemExporter
保存json数据的时候可以使用这两个类让操作变得更简单
1、JsonItemExporter
:这个是每次把数据添加到内存中,最后统一写入磁盘中。好处是存储的数据是一个满足json规则的数据,坏处是如果数据量比较大,会比较耗内存,示例代码如下
from scrapy.exporters import JsonItemExporterclass QsbkPipeline:def __init__(self):self.f = open('duanzi.json', 'wb')self.exporter = JsonItemExporter(self.f, ensure_ascii=False, encoding='utf-8')self.exporter.start_exporting()def open_spider(self, spider):print('爬虫开始。。')def process_item(self, item, spider):self.exporter.export_item(item)return itemdef close_spider(self, spider):self.exporter.finish_exporting()self.f.close()print('爬虫结束了。。。')
2、JsonLinesItemExporter
:这个是每次调用export_item
的时候就把这个item存储到硬盘中。坏处是每一个字典一行,整个文件不是一个满足json格式的文件。好处是每次处理数据的时候就直接存储到了硬盘中,这样不会耗内存,数据也比较安全。示例代码如下
from scrapy.exporters import JsonLinesItemExporterclass QsbkPipeline:def __init__(self):self.f = open('duanzi.json', 'wb')self.exporter = JsonLinesItemExporter(self.f, ensure_ascii=False, encoding='utf-8')def open_spider(self, spider):print('爬虫开始。。')def process_item(self, item, spider):self.exporter.export_item(item)return itemdef close_spider(self, spider):self.f.close()print('爬虫结束了。。。')
CrawlSpider
在上一个糗事百科的爬虫案例中。我们是自己在解析完整个页面后获取下一页的url,然后重新发送一个请求。有时候我们想要这样做,只要满足某个条件的url,都给我进行爬取。那么这时候我们就可以通过CrawlSpider
来帮我们完成了。CrawlSpider
继承自Spider
,只不过是在之前的基础之上增加了新的功能,可以定义爬取的url的规则,以后scrapy碰到满足条件的url都进行爬取,而不用手动的yield Request
。
CrawlSpider爬虫:
创建CrawlSpider爬虫:
之前创建爬虫的方式是通过scrapy genspider [爬虫名字] [域名]
的方式创建的。如果想要创建CrawlSpider
爬虫,那么应该通过以下命令创建:
scrapy genspider -t crawl [爬虫名字] [域名]
LinkExtractors链接提取器:
使用LinkExtractors
可以不用程序员自己提取想要的url,然后发送请求。这些工作都可以交给LinkExtractors
,他会在所有爬的页面中找到满足规则的url
,实现自动的爬取。以下对LinkExtractors
类做一个简单的介绍:
class scrapy.linkextractors.LinkExtractor(allow = (),deny = (),allow_domains = (),deny_domains = (),deny_extensions = None,restrict_xpaths = (),tags = ('a','area'),attrs = ('href'),canonicalize = True,unique = True,process_value = None
)
主要参数讲解:
- allow:允许的url。所有满足这个正则表达式的url都会被提取。
- deny:禁止的url。所有满足这个正则表达式的url都不会被提取。
- allow_domains:允许的域名。只有在这个里面指定的域名的url才会被提取。
- deny_domains:禁止的域名。所有在这个里面指定的域名的url都不会被提取。
- restrict_xpaths:严格的xpath。和allow共同过滤链接。
Rule规则类:
定义爬虫的规则类。以下对这个类做一个简单的介绍:
class scrapy.spiders.Rule(link_extractor, callback = None, cb_kwargs = None, follow = None, process_links = None, process_request = None
)
主要参数讲解:
- link_extractor:一个
LinkExtractor
对象,用于定义爬取规则。 - callback:满足这个规则的url,应该要执行哪个回调函数。因为
CrawlSpider
使用了parse
作为回调函数,因此不要覆盖parse
作为回调函数自己的回调函数。 - follow:指定根据该规则从response中提取的链接是否需要跟进。
- process_links:从link_extractor中获取到链接后会传递给这个函数,用来过滤不需要爬取的链接。
微信小程序社区CrawlSpider案例
主程序代码如下
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from ..items import WxappItemclass WxappApiderSpider(CrawlSpider):name = 'wxapp_apider'allowed_domains = ['wxapp-union.com']start_urls = ['http://www.wxapp-union.com/portal.php?mod=list&catid=2&page=1']rules = (Rule(LinkExtractor(allow=r'.+mod=list&catid=2&page=\d'), follow=True),Rule(LinkExtractor(allow=r'.+article-.+\.html'), callback='parse_item', follow=True),)def parse_item(self, response):title=response.xpath("//h1[@class='ph']/text()").get()authors_p=response.xpath("//p[@class='authors']")author=authors_p.xpath(".//a/text()").get()pub_time=authors_p.xpath(".//span/text()").get()content=response.xpath("//td[@id='article_content']//text()").getall()content="".join(content)item=WxappItem(title=title,author=author,pub_time=pub_time,content=content)yield item
1、其中LinkExtractor
中的allow
中填写允许的域名,其中填写正则表达式,满足正则表达式的都会被提取。
2、什么情况下使用follow,如果在爬取页面的时候,需要将满足条件的url再进行跟进,设置为True,否则设置为False。
3、什么情况下使用callback:如果这个url对应的页面,只是为了获取更多的url,并不需要里面具体的数据,那么可以不指定callback。
scrapy shell
1、可以方便的做一些数据提取的测试代码。
2、如果想要执行scrapy命令,要先进入到scrapy所在的环境中。
3、如果想要夺取某个项目的配置信息,那么应该先禁图到这个项目中,再执行scrapy shell
命令。
scrapy shell 'url地址'
scrapy模拟登陆人人网
1、想要发送post请求,那么推荐使用scrapy FormRequest0
方法,可以方便的指定表单数据。
2、如果想在爬虫一开始的时候就发送post请求,吗么应该重写start_requests
方法,在这个方法中发送post请求,代码如下:
import scrapyclass RenrenSpider(scrapy.Spider):name = 'renren'allowed_domains = ['renren.com']start_urls = ['http://renren.com/']
#此处为重写start_requests方法,发送post请求登陆def start_requests(self):url='http://www.renren.com/PLogin.do'data = {'email': '手机号','password': '密码'}request=scrapy.FormRequest(url=url,formdata=data,callback=self.parse_page)yield requestdef parse_page(self,response):request=scrapy.Request(url='http://www.renren.com/id/profile',callback=self.parse_profile)yield requestdef parse_profile(self,response):with open('dapeng.html','w',encoding='utf-8') as f:f.write(response.text)
scrapy 下载文件和图片
下载文件的 Files Pipeline
当使用Files Pipeline
下载文件的时候,按照以下步骤来完成:
- 定义好一个
Items
,然后在这个item
中定义两个属性,分别为file_urls
以及files
。file_urls
是用来存储需要下载的图片的url链接,需要给一个列表。 - 当文件下载完成后,会把文件下载的相关信息存储到
item
的files
属性中。比如下载路径、下载的url和文件的校验码等。 - 在配置文件
settings.py
中配置FILES_STORE
,这个配置是用来设置文件下载下来的路径。 - 启动
pipeline
:在ITEM_PIPELINES
中设置scrapy.pipelines.files.FilesPipeline:1
。
下载图片的 Image Pipeline
当使用Image Pipeline
下载文件的时候,按照以下步骤来完成:
- 定义好一个Item,然后在这个 Item 中定义两个属性,分别为
image_urls
以及images
。image_urls
是用来存储需要下载的图片的url链接,需要给一个列表。 - 当文件下载完成后,会把文件下载的相关信息存储到
item
的images
属性中。比如下载路径、下载的url和文件的校验码等。 - 在配置文件
settings.py
中配置IMAGES_STORE
,这个配置是用来设置文件下载下来的路径。 - 启动
pipeline
:在ITEM_PIPELINES
中设置scrapy.pipelines.images.ImagesPipeline:1
。
map函数
srcs = list(map(lambda x: response.urljoin(x), srcs))
map(function, iterable, ...)
- function – 函数
- iterable – 一个或多个序列
map(lambda x: x ** 2, [1, 2, 3, 4, 5]) # 使用 lambda 匿名函数
[1, 4, 9, 16, 25]
User-Agent随机请求头
http://www.useragentstring.com
1、Middleware.py文件如下设置
class UserAgentDownloadMiddleware(object):USER_AGENTS = ['Mozilla/5.0 (compatible; MSIE 9.0; AOL 9.0; Windows NT 6.0; Trident/5.0)','Mozilla/5.0 (compatible; MSIE 9.0; AOL 9.1; AOLBuild 4334.5012; Windows NT 6.0; WOW64; Trident/5.0)','Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:78.0) Gecko/20100101 Firefox/78.0','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36','Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.9a3) Gecko/20070409 BonEcho/2.0.0.3','Mozilla/5.0 (Windows NT 10.0; WOW64; rv:77.0) Gecko/20100101 Firefox/77.0']def process_request(self, request, spider):user_agent = random.choice(self.USER_AGENTS)request.headers['User-Agent'] = user_agent
注:USER_AGENTS必须为列表,才能使用random.choice
进行选择
2、爬虫文件如下配置
def parse(self, response):user_agent = json.loads(response.text)['user-agent']print(user_agent)yield scrapy.Request(self.start_urls[0], dont_filter=True)
其中yield
为一直发送请求
dont_filter=True
为设置不删除重复的链接,即可以一直访问相同的链接。
设置代理方法:
request.mate['proxy']=proxy
scrapy通过twisted实现异步存入数据库
代码如下:
from twisted.enterprise import adbapi
from pymysql import cursors
class JianshuTwistedPipeline(object):def __init__(self):dbparms = {'host': '127.0.0.1','user': 'root','password': 'root','database': 'jianshu','port': 3306,'charset': 'utf8',# 此处必须填写cursor类,否则回直接使用默认cursor类'cursorclass': cursors.DictCursor}self.dbpool = adbapi.ConnectionPool('pymysql', **dbparms)self._sql = None@propertydef sql(self):if not self._sql:self._sql = """insert into article(id,title,content,origin_url,article_id) values (null ,%s,%s,%s,%s)"""return self._sqlreturn self._sqldef process_item(self, item, spider):# 此处为添加item,将调价item给self.insert_item函数处理defer = self.dbpool.runInteraction(self.insert_item, item)defer.addErrback(self.handle_error, item, spider)return item# 定义插入数据库代码def insert_item(self, cursor, item):cursor.execute(self.sql, (item['title'], item['content'], item['origin_url'], item['article_id']))# 处理错误代码def handle_error(self, error, item, spider):print('=' * 10 + 'error' + '=' * 10)print(error)print('=' * 10 + 'error' + '=' * 10)