网络爬虫
爬虫类型
- 通用网络爬虫:如搜索引擎,面向关键字,目标是尽可能大的网络覆盖率,侧重广度
- 聚焦网络爬虫:抓取某一特定主题相关的网络资源
- 增量式网络爬虫:对已经爬取的网页进行增量式更新,只爬取新产生或发生变化的网页。
- 深层网络爬虫:不能通过静态链接获取的,隐藏在搜索表单之后的,如需要登录后才可以查看到的资源。
urllib
from urllib import request,parse
url = r'http://www.baidu.com'
postdata = parse.urlencode([('wd','china')]) #post提交的参数
#构造请求
req = request.Request(url)
#添加HTTP头来模拟浏览器
req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36')
#当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器我是从哪个页面链接过来的,用于页面统计和资源防盗链
req.add_header('Referer', 'https://passport.weibo.cn/signin/login?entry=mweibo&res=wel&wm=3349&r=http%3A%2F%2Fm.weibo.cn%2F')
#获得响应
with request.urlopen(req,data=postdata.encode('utf-8')) as f:
print(f.status)
for k,v in f.getheaders():
print('%s %s' %(k,v))
print(f.read().decode('utf-8'))
requests
import requests
user_agent = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36'
headers = {'User-Agent':user_agent}
kv = {'wd':'china'}
r = requests.get('http://www.baidu.com/s',params=kv,headers = headers)
print(r.status_code )
if r.status_code == 200:
print(r.headers)
print(r.headers.get('content-type'))
else:
r.raise_for_status()
#获取cookie
for cookie in r.cookies.keys():
print(cookie + ':' + r.cookies.get(cookie))
print(r.url)
print(r.content) #字节形式
print(r.text[-100:]) #文本形式
print(r.encoding) #根据头部信息猜测的编码方式,不一定准确
print(r.apparent_encoding) #根据内容猜测的编码方式
r.encoding = r.apparent_encoding
import requests
url = 'http://www.baidu.com'
s = requests.Session()
#首先访问,服务器会先分配一个cookie
re = s.get(url,allow_redirects=True)
user_agent = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36'
headers = {'User-Agent':user_agent}
kv = {'wd':'china'}
#在访问就带有cookie了,不会视为非法用户
re = s.get('http://www.baidu.com/s',params=kv,headers = headers)
print(re.status_code )
import requests
url = 'http://github.com'
proxies = {
"http":"http://user:pass@host/",
}
r = requests.get(url,timeout = 2,proxies=proxies)
print(r.history)
import requests
cs_url = 'http://httpbin.org/post'
my_data = {
'key1' : 'value1',
'key2' : 'value2'
}
r = requests.post (cs_url, data = my_data)
print(r.content)
BeautifulSoup
from _pytest.capture import unicode
from bs4 import BeautifulSoup
soup = BeautifulSoup(open('index.html'),'lxml')
#print(soup.prettify())
# print(soup.title)
# print(soup.title.name)
# soup.title.name = 'mytitle'
# print(soup.mytitle.name)
# print(soup.a)
# print(soup.p.attrs)
# print(soup.p.get('class'))
# print('-----------------------')
# print(soup.p.string) #NavigableString
# unicode_str = unicode(soup.p.string)
# print(unicode_str)
#子节点
# print(soup.head.contents)
# print(soup.head.contents[0].string) #.string : 如果一个标记里面没有标记了或者只有唯一标记,则会返回最里面的内容,否则包含多个子节点,则tag无法确定,返回None
# for child in soup.head.children:
# print(child)
# for child in soup.head.descendants:
# print(child)
for string in soup.strings: #.stripped_strings去掉空格和空行
print(string)
for parent in soup.a.parents:
if parent is None:
print('1' + parent)
else:
print(parent.name)
#兄弟节点
print(soup.p.next_sibling)
print(soup.p.prev_sibling)
for sibling in soup.a.next_siblings:
print(repr(sibling))
#前后节点
for ele in soup.a.next_elements:
print(ele)
from _pytest.capture import unicode
from bs4 import BeautifulSoup
import re
from pip._vendor.distlib._backport.tarfile import TUREAD
soup = BeautifulSoup(open('index.html'),'lxml')
print(soup.findAll('b')) #寻找所有的<b>标记
print(soup.findAll(re.compile('^b'))) #寻找所有以b开头的标记
print(soup.findAll(['a','b'])) #找到所有的a标记和b标记
print(soup.findAll(True)) #找到所有tag
def hasClass_Id(tag):
return tag.has_attr('class') and tag.has_attr('id')
print(soup.findAll(hasClass_Id)) #定义过滤器,找到包含class和id属性的元素
#-------------------------------
print(soup.findAll(id='link1'))
print(soup.findAll(href=re.compile("163"))) #查找href属性含有“163”的tag
print(soup.findAll('a',class_='py1')) #用class过滤
data_soup = BeautifulSoup('<div data-foo="value">foo!</div>','lxml')
print(data_soup.find_all(attrs={"data-foo":"value"}) )#定义字典参数来搜索包含特殊属性的tag
print(soup.find_all('a',text=re.compile('^A'))) #text参数用来搜索字符串
print(soup.find_all('title',recursive=False)) #只搜索tag的直接子节点
xpath,css选择器
from _pytest.capture import unicode
from bs4 import BeautifulSoup
import re
from pip._vendor.distlib._backport.tarfile import TUREAD
soup = BeautifulSoup(open('index.html'),'lxml')
#css选择器
print(soup.select("title")) #直接插找title标记
print(soup.select("html head title")) #逐层查找title
print(soup.select("head > title")) #查找head下的title
print(soup.select("p > # link1")) #查找p下id为link1的标记
print(soup.select(".course")) #根据css类名查找
print(soup.select("# link1")) #根据tag的id找
print(soup.select('a[href]')) #根据是否存在某个属性来查找
import json
from bs4 import BeautifulSoup
import requests
url = 'http://seputu.com/'
user_agent = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36'
headers = {'User-Agent':user_agent}
r = requests.get(url,headers=headers)
r.encoding = r.apparent_encoding
# print(r.text)
soup = BeautifulSoup(r.text,'html.parser',from_encoding='utf-8')
content = []
for mulu in soup.findAll(class_='mulu'):
h2 = mulu.find('h2')
if h2 != None:
h2_title = h2.string
list = []
for a in mulu.find(class_='box').find_all('a'):
href = a.get('href')
box_title = a.get('title')
list.append({'href':href,'box_title':box_title})
content.append({'title':h2_title,'content':list})
with open('qiye.json','w') as fp:
json.dump(content,fp=fp,indent=4)
import csv
import json
import re
from bs4 import BeautifulSoup
import requests
url = 'http://seputu.com/'
user_agent = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36'
headers = {'User-Agent':user_agent}
r = requests.get(url,headers=headers)
r.encoding = r.apparent_encoding
# print(r.text)
soup = BeautifulSoup(r.text,'html.parser',from_encoding='utf-8')
pattern = re.compile(r'\s*\[(.*)\]\s+(.*)')
rows = []
for mulu in soup.findAll(class_='mulu'):
h2 = mulu.find('h2')
if h2 != None:
h2_title = h2.string
for a in mulu.find(class_='box').find_all('a'):
href = a.get('href')
box_title = a.get('title')
match = pattern.search(str(box_title))
if match != None:
date = match.group(1)
real_title = match.group(2)
content = (h2_title,real_title,href,date)
print(content)
rows.append(content)
result_header = ['title','real_title','href','date']
with open('qiye.csv','w') as f:
f_csv = csv.writer(f,)
f_csv.writerow(result_header)
f_csv.writerows(rows)
爬取图片等多媒体文件 urllib.request.urlretrieve
import csv
import json
import re
import urllib
from bs4 import BeautifulSoup
import requests
def schedue(a,b,c):
'''''回调函数
@a:已经下载的数据块
@b:数据块的大小
@c:远程文件的大小
'''
per=100.0*a*b/c
if per>100:
per=100
print('%.2f%%' % per)
url = 'http://www.ivsky.com/bizhi/fengjing/'
user_agent = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36'
headers = {'User-Agent':user_agent}
r = requests.get(url,headers=headers)
r.encoding = r.apparent_encoding
# print(r.text)
soup = BeautifulSoup(r.text,'html.parser',from_encoding='utf-8')
for tupian in soup.findAll(class_='left'):
for img in tupian.findAll('img'):
urllib.request.urlretrieve(img.get('src'),img.get('alt')+'.jpg',schedue)
基础爬虫框架
五大模块:
- 爬虫调度器
- URL管理器:链接去重:1)内存去重,如set; 2)关系数据库去重; 3)缓存数据库去重,大部分成熟爬虫采用。
- HTML下载器、
- HTML解析器、
- 数据存储器
动态网站抓取
动态网站数据是局部更新。
两种做法:
- 直接从JavaScript采集加载的数据
- 直接采集浏览器中加载好的数据
网页登录POST分析
利用Chrome浏览器F12控制台Network,Serarch进行分析。
如登陆,如果用户名和密码都是用的明文传输,在手动登陆的时候有个小技巧,那就是故意把密码填错,这样可以很容易看到用户名和密码正确的提交路径和提交方式。注意要在打开目标网站登陆界面之前就要打开抓包工具,因为一般加密登陆用户名和密码的js代码在用户登陆之前就被请求了。如果在手动登陆的过程再打开,那么可能就找不到它的JS加密文件了。这种情况一般用于加密提交用户名和密码的时候。
POST中的参数只有三种情况:
验证码问题
- IP代理
使用开源项目IPProxyPool代理池
import requests
import json
r = requests.get('http://127.0.0.1:8000/?types=0&count=5&country=国内')
ip_ports = json.loads(r.text)
print(ip_ports)
ip = ip_ports[0][0]
port = ip_ports[0][1]
proxies={
'http':'http://%s:%s'%(ip,port),
'https':'http://%s:%s'%(ip,port)
}
r = requests.get('http://ip.chinaz.com/',proxies=proxies)
r.encoding='utf-8'
print(r.text)
- Cookie登录
将cookie信息保存到本地,可以保存一段时间,下次登录直接使用cookie。 - 传统验证码识别
使用图像识别。 - 人工打码
打码兔,QQ超人打码等平台,自动识别+人工识别的组合方式。 - 滑动验证打码
使用selenium:
- 在浏览器上模拟鼠标拖动
- 计算图片中缺口偏移
- 模拟人类拖动鼠标的轨迹
还可以采取多账号登录后,保存cookie信息,组建cookie池。
一般爬取难度 :www > m > wap
终端协议分析
当网页抓取困难时,可以考虑PC端或者APP端。
PC端可以使用HTTPAnalyzer分析获取数据api;
app可以在模拟器上使用,然后使用Wireshark抓包分析。
Scrapy
各大组件和功能:
- Scrapy引擎(Engine):控制数据流在系统的所有组件中流动,并在相应动作发生时触发事件。
- 调度器(Scheduler):从引擎接收request并让它们入队,一般以后引擎请求request时提供给引擎。
- 下载器(Downloader)
- Spider :用户编写用于分析Response并提取item或额外跟进的url的类。相当于Html解析器
- Item Pipeline:负责处理Spider提取出的item,如清理验证及持久化(如存储在数据库中)。相当于数据存储器。
- 下载器中间件(Downloader middlewares):引擎与下载器之间的特定钩子,处理下载器传递给引擎的response。
- Spider中间件(Spider middlewares):引擎与Spider之间的特定钩子,处理Spider的输入(response)和输出(Itme及requests)。
windows下安装
需要
- pywin32
-
pyOpenSSL:下载完成后运行
python setup.py install
- lxml:
pip install lxml' 最后安装Scrapy:
pip install Scrapywindows上安装Scrapy确实比较坑,Scrapy依赖Twisted,尤其是在安装Twisted中会出现问题。 在我安装的过程中,直接使用
pip install Scrapy` 进行安装时,直接pip install需要在本地进行编译,我电脑上studio 2015各种报错,比如
error: command 'cl.exe' failed: No such file or directory
在网上搜了好多,最终在知乎找到了一种“曲线救国”的解决办法,如下:
Python Extension Packages for Windows
去上面地址下载你对应版本cp35的whl,注意,虽然你系统是64位,但要看清你python版本是32还是64位的,再下载对应的win32或者amd64文件
安装wheel
pip install wheel
进入.whl所在的文件夹,执行命令即可完成安装
pip install 带后缀的完整文件名
然后再使用pip install Scrapy
去安装就ok啦。
网友评论