爬虫

网络爬虫

爬虫类型

  • 通用网络爬虫:如搜索引擎,面向关键字,目标是尽可能大的网络覆盖率,侧重广度
  • 聚焦网络爬虫:抓取某一特定主题相关的网络资源
  • 增量式网络爬虫:对已经爬取的网页进行增量式更新,只爬取新产生或发生变化的网页。
  • 深层网络爬虫:不能通过静态链接获取的,隐藏在搜索表单之后的,如需要登录后才可以查看到的资源。

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')) #寻找所有的标记
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('
foo!
','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中的参数只有三种情况:

  1. 在源代码页面中的
  2. 是通过服务器返回的
  3. 通过运行js生成的 。
    这部分还没能够完全掌握,仍需要好好练习。
    参考:
    微博模拟登录
    百度云盘模拟登录

验证码问题

  1. 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)
  1. Cookie登录
    将cookie信息保存到本地,可以保存一段时间,下次登录直接使用cookie。
  2. 传统验证码识别
    使用图像识别。
  3. 人工打码
    打码兔,QQ超人打码等平台,自动识别+人工识别的组合方式。
  4. 滑动验证打码
    使用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啦。

你可能感兴趣的:(爬虫)