Python爬虫总结(CSS,Xpath,JsonLoad;静态网页,JS加载,Ajax异步请求)

在这里插入图片描述

前言

随着人类社会的高速发展,数据对各行各业的重要性,愈加突出。爬虫,也称为数据采集器,是指通过程序设计,机械化地对网络上的数据,进行批量爬取,以代替低效的人工获取信息的手段。

1. 道德法律问题

爬虫目前在法律上尚属灰色地段,但爬别的网站用于自己的商业化用途也可能存在着法律风险。非法抓取使用“新浪微博”用户信息被判赔200万元,这是国内的一条因爬虫被判败诉的新闻。所以各商业公司还是悠着点,特别是涉及隐私数据。
大型的网站一般都会有robot.txt,这算是与爬虫者的一个协议。只要在robot.txt允许的范围内爬虫就不存在道德和法律风险。

2. 网络爬虫步骤

2.1 检查API接口

API是网站官方提供的数据接口,如果通过调用API采集数据,则相当于在网站允许的范围内采集。这样既不会有道德法律风险,也没有网站故意设置的障碍;不过调用API接口的访问则处于网站的控制中,网站可以用来收费,可以用来限制访问上限等。整体来看,如果数据采集的需求并不是很独特,那么有API则应优先采用调用API的方式。如果没有,则选择爬虫。

2.2 数据获取渠道分析

页面包含数据
这种情况是最容易解决的,一般来讲基本上是静态网页,或者动态网页,采用模板渲染,浏览器获取到HTML的时候已经是包含所有的关键信息,所以直接在网页上看到的内容都可以通过特定的HTML标签得到

JavaScript代码加载内容
虽然网页显示的数据在HTML标签里面,但是指定HTML标签下内容为空。这是因为数据在js代码里面,而js的执行是在浏览器端的操作。当我们用程序去请求网页地址的时候,得到的response是网页代码和js的代码,因此自己在浏览器端能看到数据,解析时由于js未执行,指定HTML标签下数据肯定为空。这个时候的处理办法:找到包含内容的js代码串,然后通过正则表达式获得相应的内容,而不是解析HTML标签。

Ajax异步请求
这种情况是现在很常见的,尤其是在数据以分页形式显示在网页上,并且页面无刷新,或者是对网页进行某个交互操作后得到数据。所以当我们开始刷新页面的时候就要开始跟踪所有的请求,观察数据到底是在哪一步加载进来的。然后当我们找到核心的异步请求的时候,就只用抓取这个异步请求就可以了,如果原始网页没有任何有用信息,也没必要去抓取原始网页了。

2.3 页面数据结构分析

结构性数据
结构化的数据是最好处理,一般都是类似JSON格式的字符串,直接解析JSON数据就可以了,提取JSON的关键字段即可。

page = requests.get(url)
headers = {}
page.encoding = 'utf-8'
data =re.findall(r'<script>__INITIAL_STATE__=(.*?)</script>',page.text)[0]
json_data = json.loads(data)
print(json_data)
#f = open('结果2.txt', 'w',
encoding='utf-8')  # 以'w'方式打开文件
#for k, v in json_data.items(): 
# 遍历字典中的键值
#s2 = str(v)  # 把字典的值转换成字符型
#f.write(k + '\n')  # 键和值分行放,键在单数行,值在双数行
#f.write(s2 + '\n')
jobList = json_data['souresult']['Items']    #打印json_data,抓到关键词
for element in jobList:
    print(f"===公司名称:{element['CompanyName']}:===\n"
    f"岗位名称:{element['DateCreated']}\n"
    f"招聘人数:{element['JobTitle']}\n"
    f"工作代码:{element['JobTypeName']}\n"
    f"公司代码:{element['RecruitCount']}\n"
    f"详细信息URL:{element['SocialCompanyUrl']}")

非结构性数据-HTML文本数据
HTML文本基本上是传统爬虫过程中最常见的,也就是大多数时候会遇到的情况。例如抓取一个网页,得到的是HTML,然后需要解析一些常见的元素,提取一些关键的信息。HTML其实理应属于结构化的文本组织,但是又因为一般我们需要的关键信息并非直接可以得到,需要进行对HTML的解析查找,甚至一些字符串操作才能得到,所以还是归类于非结构化的数据处理中。常见解析方法:
CSS选择器
现在的网页样式比较多,所以一般的网页都会有一些CSS的定位,例如class,id等等,或者我们根据常见的节点路径进行定位。

item = soup.select('#u1 > a') 
#选择指定目录下所有css数据
#print([i for i in item])   #print里添加循环时,记得加方括号
item = soup.select_one('#u1 > a')    #选择指定目录下第一条 css数据
print(item)

Findall

##招聘人数
recru_num = soup.find_all('div', attrs={'class':'cityfn-left'})   #找到页面中a元素的所有元素,并找到a元素中 属性为'class=value'———————— attrs={"class": 'value'}
print(recru_num)
dr = re.compile(r'<[^>]+>', re.S)
data = dr.sub('', str(recru_num))        #过滤HTML标签
print(data)

Xpath

html = etree.HTML(wb_data)
html_data = html.xpath('/html/body/div/ul/li/a/text()')  #获取某个标签的内容(基本使用)

正则表达式
正则表达式,用标准正则解析,一般会把HTML当做普通文本,用指定格式匹配。当相关文本是小片段文本,或者某一串字符,或者HTML包含javascript的代码,无法用CSS选择器或者XPATH。

import re
a = '<html><body><p>[<a href="/aero-desktop-wallpapers.html" title="Aero HD Wallpapers">Aero</a>, <a href="/animals-desktop-wallpapers.html" title="Animals HD Wallpapers">Animals</a>, <a href="/architecture-desktop-wallpapers.html" title="Architecture HD Wallpapers">Architecture</a>,Wallpapers">Artistic</a>, ........(省略)......... <a href="/vintage-desktop-wallpapers.html" title="Vintage HD Wallpapers">Vintage</a>]</p></body></html>'
titles = re.findall('<a href="/(.*?)-desktop-wallpapers.html',str(a))
print (titles)


运行结果:
['aero', 'animals', 'architecture', 'vintage']

具体路径都在 Chrome-检查元素-copy 可以快速得到,

3. 爬虫知识相关补充

3.1 了解网络请求

刚刚一直在宽泛的提到一些我们需要找到请求,对于请求只是一笔带过,但请求是很重要的一部分,包括如何绕过限制,如何发送正确地数据,都需要对的请求,这里就要详细的展开介绍请求,以及如何模拟请求。

我们常说爬虫其实就是一堆的HTTP请求,找到待爬取的链接,然后发送一个请求包,得到一个返回包,所以核心的几个要素就是:

  1. URL
  2. 请求方法(POST, GET)
  3. 请求包headers
  4. 请求包内容
  5. 返回包headers

在用Chrome进行网络请求捕获或者用抓包工具分析请求时,最重要的是弄清楚URL,请求方法,然后headers里面的字段,大多数出问题就出在headers里面,最常限制的几个字段就是User-Agent, Referer, Cookie 另外Base Auth也是在headers里面加Autheration的字段。

GET提交,请求的数据会附在URL之后(就是把数据放置在HTTP协议头中),以?分割URL和传输数据,多个参数用&连接;
POST提交:把提交的数据放置在是HTTP包的包体中。
对于GET:

import urllib.request
import urllib.parse

url='http://127.0.0.1:8899/login?name=admin&pwd=admin'  #调用的网址
# 设置header头跟post请求方式相同
#headers={"User-Agent": "Mozilla...."}
# req =urllib.request.Request(r'https://www.baidu.com/',headers=headers)
print("get请求")
web=urllib.request.urlopen(url)
print("二进制返回结果")
print(web)
print("解析后的结果")
f=web.read()

对于POST:

# post请求参数需要准成二进制类型
params = bytes(urllib.parse.urlencode({'name':'admin','pwd':'admin'}), encoding=  'utf8')
print("post请求")
web = urllib.request.urlopen('http://127.0.0.1:8899/login',params)
#获取结果
f = web.read()
print(f)

请求数据也就是post时需要发送的数据,一般都是将Key-Value进行urlencode。返回包headers大多数会被人忽视。其实很多时候会发现明明请求方法还有请求包的内容都对了,为什么没有返回内容,或者发现请求被限制,其实这里大概有两个原因:
一个是返回包的内容是空的,但是在返回包的headers的字段里面有个Location,这个Location字段就是告诉浏览器重定向,所以有时候代码没有自动跟踪,自然就没有内容了;
另外一个就是Cookie问题,简单说就是浏览器为什么知道你的请求合法的,例如已登录等等,其实就是可能你之前某个请求的返回包的headers里面有个字段叫Set-Cookie,Cookie存在本地,一旦设置后,除非过期,一般都会自动加在请求字段上,所以Set-Cookie里面的内容就会告诉浏览器存多久,存的是什么内容,在哪个路径下有用,Cookie都是在指定域下,一般都不跨域,域就是你请求的链接host。

所以分析请求时,一定要注意前四个,在模拟时保持一致,同时观察第五个返回时是不是有限制或者有重定向。

3.2 爬虫效率问题

多线程:

threads= []
while threads or crawl_queue:
    for thread in threads:
        if not thread.is_alive():
            threads.remove(thread)
    while len(threads) < max_threads and crawl_queue:
        thread =threading.Thread(target=process_queue)
        thread.setDaemon(True) # set daemon so main thread can exit when receives ctrl-c
        thread.start()
        threads.append(thread)
  time.sleep(SLEEP_TIME)

多进程:

def process_crawler(args, **kwargs):
    num_cpus = multiprocessing.cpu_count()
    #pool =multiprocessing.Pool(processes=num_cpus)
    print'Starting {} processes'.format(num_cpus))
    processes = []
    for i in range(num_cpus):
        p =multiprocessing.Process(target=threaded_crawler, args=[args], kwargs=kwargs)
        #parsed =pool.apply_async(threaded_link_crawler, args, kwargs)
        p.start()
        processes.append(p)
    # wait for processes to complete
    for p in processes:
        p.join()

4. Ant-反爬虫方法

Headers
加上了请求头就可以伪装成浏览器,混过反爬的第一道关卡。一般的网站加上User-Agent就可以,反爬严格的网站则要加上cookie甚至各种参数都要加上。

随机延时
稳定性是大规模爬虫的另一个核心问题,虽然与效率冲突。许多网站都会统计同一个IP一段时间内的访问频率,如果采集过快,会直接封禁IP。一般情况下延时3~5秒足够了。

IP代理池
如果每次访问设置的随机延时会成为额外大量的时间成本,并且单个IP快速访问会有被封的风险,这时要用代理池。有两点好处:一是降低某个IP单位时间内的访问频率,降低被封风险;二是即使IP被封,也有别的IP可以继续访问。

模拟登录
请求头的cookie含有登录信息,其中存在cookie寿命长短问题,cookie寿命较长,可以直接在网站上人工登录然后把cookie复制到代码中;而有些
cookie寿命短,只有6分钟的寿命,而且一个帐号一天只能访问一定次数,超过就封号,无论爬得再慢——这种情况下只能选择账号池进行自动登录并不断切换。

模拟浏览器
各种JS代码逻辑过于复杂。模拟浏览器通过下载浏览器驱动,可以伪装成正常的用户访问,所以可以跳过大部分的反爬机制,不过缺点也很明显,就是慢。所以,能用requests搞定的优先用requests,实在没有办法了再考虑模拟浏览器。

验证码
Python自动识别图像的包,不过对于大部分网站的验证码都无能为力。对于验证码问题,首先是躲过去尽量不要触发验证码,实在触发了只能乖乖人工去填验证码。

以上,是本人整理的Python爬虫总结,欢迎大家交流讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值