python爬虫编程学习小结

一、基础知识总结

1.1 HTTP

HTTP是一个客户端(用户)和服务器端(网站)之间进行请求和应答的标准。通过使用网页浏览器、网络爬虫或者其他工具,客户端可以向服务器上的指定端口(默认端口为80)发起一个HTTP请求。这个客户端成为客户代理(user agent)。应答服务器上存储着一些资源码,比如HTML文件和图像。这个应答服务器成为源服务器(origin server)。在用户代理和源服务器中间可能存在多个“中间层”,比如代理服务器、网关或者隧道(tunnel)。尽管TCP/IP是互联网最流行的协议,但HTTP中并没有规定必须使用它或它支持的层。事实上。HTTP可以在互联网协议或其他网络上实现。HTTP假定其下层协议能够提供可靠的传输,因此,任何能够提供这种保证的协议都可以使用。使用TCP/IP协议族时RCP作为传输层。通常由HTTP客户端发起一个请求,创建一个到服务器指定端口(默认是80端口)的TCP链接。HTTP服务器则在该端口监听客户端的请求。一旦收到请求,服务器会向客户端返回一个状态(比如“THTTP/1.1 200 OK”),以及请求的文件、错误信息等响应内容。

HTTP的主要请求方法:

  • GET:向指定的资源发出“显示”请求。GET方法应该只用于读取数据,而不应当被用于“副作用”的操作中(例如在Web
    Application中)。其中一个原因是GET可能会被网络蜘蛛等随意访问。

  • HEAD:与GET方法一样,都是向服务器发出直顶资源的请求,只不过服务器将不会出传回资源的内容部分。它的好处在于,使用这个方法可以在不必传输内容的情况下,将获取到其中“关于该资源的信息”(元信息或元数据)。

  • POST:向指定资源提交数据,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求文本中。这个请求可能会创建新的资源或修改现有资源,或二者皆有。

  • PUT:向指定资源位置上传输最新内容。DELETE:请求服务器删除Request-URL所标识的资源,或二者皆有。

  • TRACE:回显服务器收到的请求,主要用于测试或诊断。

  • OPTIONS:这个方法可使服务器传回该资源所支持的所有HTTP请求方法。用“*”来代表资源名称向Web服务器发送OPTIONS请求,可以测试服务器共能是否正常。

  • CONNECT:HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。通常用于SSL加密服务器的连接(经由非加密的HTTP代理服务器)。方法名称是区分大小写的。当某个请求所针对的资源不支持对应的请求方法的时候,服务器应当返回状态码405(Method Not Allowed),当服务器不认识或者不支持对应的请求方法的时候,应当返回状态码501(Not Implemented)。

1.2 网页基础

网页是由 HTML 、 CSS 、JavaScript 组成的。

HTML 是用来搭建整个网页的骨架,而 CSS 是为了让整个页面更好看,包括我们看到的颜色,每个模块的大小、位置等都是由 CSS 来控制的, JavaScript 是用来让整个网页“动起来”,这个动起来有两层意思,一层是网页的数据动态交互,还有一层是真正的动,比如我们都见过一些网页上的动画,一般都是由 JavaScript 配合 CSS 来完成的。

网页结构

示例

<!DOCTYPE  html>  # DOCTYPE 定义了文档类型是 html
<html>
    <head>   # 在 head 头中,我们一般会指定当前的编码格式为 UTF-8 ,并且使用 title 来定义网页的标题,这个会显示在浏览器的标签上面。
        <meta charset="UTF-8">
        <title>Demo</title>
    </head>
    <body>  #body 中的内容一般为整个 html 文档的正文,html的标签由<h1>到<h6>六个标签构成,字体由大到小递减,换行标签为<br>,链接使用<a>来创建,herf属性包含链接的URL地址
        <div id="container">
            <div class="wrapper">
                <h1>Hello World</h1>
                <div>Hello Python.</div>
            </div>
        </div>
    </body>
</html>

  • id属性为元素提供在全文档内的唯一标识。它用于识别元素,以便样式表可以改变其外观属性,脚本可以改变、显示或删除其内容或格式化。对于添加到页面的url,它为元素提供了一个全局唯一识别,通常为页面的子章节。

  • class属性提供了一种将类似元素分类的方式,常被用于语义化或格式化。例如,一个html文档可以指定class="标记"来表明所有具有这一类值得元素都属于文档的主文本。格式化后,这样的元素可能会聚集在一起,并作为页面脚注而不会出现在html代码中。类值也可以多值声明。如class="标记重要"将元素同时放入“标记”与“重要”两类中。

  • style属性可以将表现性质赋予一个特定原色。比起使用id或class属性从样式表中选择元素,“style”被认为是一个更好的做法。

  • tile属性用于给元素一个附加的说明。大多数浏览器中这一属性显示为工具提示。 据并提取数据进行保存的自动化程序

HTML DOM
在 HTML 中,所有标签定义的内容都是节点,它们构成了一个 HTML DOM 树。
在这里插入图片描述
开发者工具检查检查网页
在这里插入图片描述
request.get

一个网络爬虫程序最普遍的过程:

  1. 获取网页 使用 request 和 urllib
    获取网页时代码和实际代码可能会有差别,这是因为现在大多网页采用js来进行一步步的渲染,不断请求服务器获取数据填充渲染进网页,原有获取的代码只是一个空壳框架
  2. 定位所需信息
  3. 得到并处理信息
  4. 保存数据
import requests
url = 'https://www.python.org/dev/peps/pep-0020/'
res = requests.get(url)
text = res.text


with open('zon_of_python.txt', 'w') as f:      #爬取python之禅并存入txt文件
    f.write(text[text.find('<pre')+28:text.find('</pre>')-1])
print(text[text.find('<pre')+28:text.find('</pre>')-1])

import urllib    #利用python自带的urllib完成以上操作:
url = 'https://www.python.org/dev/peps/pep-0020/'
res = urllib.request.urlopen(url).read().decode('utf-8')
print(res[res.find('<pre')+28:res.find('</pre>')-1])


爬取的内容:
在这里插入图片描述
request.post
适用于Post类型请求的网站,比例:网易翻译

import requests

def translate(word):
	url="http://fy.iciba.com/ajax.php?a=fy"
	data={
        'f': 'auto',
        't': 'auto',
        'w': word,
    }
	headers={
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36',
    }#User-Agent会告诉网站服务器,访问者是通过什么工具来请求的,如果是爬虫请求,一般会拒绝,如果是用户浏览器,就会应答。
    response = requests.post(url,data=data,headers=headers)     #发起请求
    json_data=response.json()   #获取json数据
    #print(json_data)
    return json_data
    
def run(word):    
    result = translate(word)['content']['out']   
    print(result)
    return result
    
def main():
    with open('zon_of_python.txt') as f:
        zh = [run(word) for word in f]
    with open('zon_of_python_zh-CN.txt', 'w') as g:
        for i in zh:
            g.write(i + '\n')
if __name__ == '__main__':
    main()

结果
在这里插入图片描述

豆瓣电影爬取

import requests
import os

if not os.path.exists('image'):
     os.mkdir('image')

def parse_html(url):
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"}
    res = requests.get(url, headers=headers)
    text = res.text
    item = []
    for i in range(25):
        text = text[text.find('alt')+3:]
        item.append(extract(text))
    return item

def extract(text):
    text = text.split('"')
    name = text[1]
    image = text[3]
    return name, image

def write_movies_file(item, stars):
    print(item)
    with open('douban_film.txt','a',encoding='utf-8') as f:
        f.write('排名:%d\t电影名:%s\n' % (stars, item[0]))
    r = requests.get(item[1])
    with open('image/' + str(item[0]) + '.jpg', 'wb') as f:
        f.write(r.content)

def main():
    stars = 1
    for offset in range(0, 250, 25):
        url = 'https://movie.douban.com/top250?start=' + str(offset) +'&filter='
        for item in parse_html(url):
            write_movies_file(item, stars)
            stars += 1

if __name__ == '__main__':
    main()

结果
在这里插入图片描述
使用API

所谓的采集网络数据,并不一定必须从网页中抓取数据,而api(Application Programming Iterface)的用处就在这里:API为开发者提供了方便友好的接口,不同的开发者用不同的语言都能获取相同的数据。目前API一般会以XML(Extensible Markup Language,可拓展标记语言)或者JSON(JavaScript Object Notation)格式来返回服务器响应,其中JSON数据格式越来越受到人们的欢迎,我们后面的课程也会详细介绍JSON格式。

实现地理编码功能

import requests

def getUrl(*address):
    ak = ''  ## 填入你的api key
    if len(address) < 1:
        return None
    else:
        for add in address:   
            url = 'http://api.map.baidu.com/geocoding/v3/?address={0}&output=json&ak={1}'.format(add,ak)  
            yield url

def getPosition(url):
    '''返回经纬度信息'''
    res = requests.get(url)
    #print(res.text)
    json_data = eval(res.text)
     if json_data['status'] == 0:
        lat = json_data['result']['location']['lat'] #纬度
        lng = json_data['result']['location']['lng'] #经度
    else:
        print("Error output!")
        return json_data['status']
    return lat,lng

if __name__ == "__main__":
    address = ['北京市清华大学','北京市北京大学','保定市华北电力大学','上海市复旦大学','武汉市武汉大学']
    for add in address:
        add_url = list(getUrl(add))[0]
        print('url:', add_url)
        try:
            lat,lng = getPosition(add_url)
            print("{0}|经度:{1}|纬度:{2}.".format(add,lng,lat))
        except Error as e:
            print(e)

在这里插入图片描述

二、Beautiful Soup、Xpath、re应用

Beautiful Soup 是一个HTML/XML 的解析器,主要用于解析和提取 HTML/XML 数据。它基于HTML DOM 的,会载入整个文档,解析整个DOM树,因此时间和内存开销都会大很多,所以性能要低于lxml。BeautifulSoup 用来解析 HTML 比较简单,API非常人性化,支持CSS选择器、Python标准库中的HTML解析器,也支持 lxml 的 XML解析器。虽然说BeautifulSoup4 简单容易比较上手,但是匹配效率还是远远不如正则以及xpath的,一般不推荐使用,推荐正则的使用。

中国大学排名定向爬取

import requests
from bs4 import BeautifulSoup
import bs4

#1. 从网络上获取大学排名网页内容
def getHTMLText(url):
    try:
        r = requests.get(url, timeout=30) 
        r.raise_for_status()
        r.encoding = r.apparent_encoding
        return r.text
    except:
        return ""

#2. 提取网页内容中信息到合适的数据结构
def fillUnivList(ulist, html):
    soup = BeautifulSoup(html, "html.parser")
    for tr in soup.find('tbody').children: 
        if isinstance(tr, bs4.element.Tag):
            tds = tr('td')
            # 根据实际提取需要的内容,
            ulist.append([tds[0].string, tds[1].string, tds[3].string])  

#3. 利用数据结构展示并输出结果
#对中英文混排输出问题进行优化:对format(),设定宽度和添加参数chr(12288)
def printUnivList(ulist, num=20):
    tplt = "{0:^10}\t{1:{3}^10}\t{2:^10}"
    print(tplt.format('排名', '学校名称', '总分', chr(12288)))
    for i in range(num):
        u = ulist[i]
        print(tplt.format(u[0], u[1], u[2], chr(12288)))

u_info = [] # 存储爬取结果的容器
url = 'http://www.zuihaodaxue.cn/zuihaodaxuepaiming2019.html'
html = getHTMLText(url)
fillUnivList(u_info, html) # 爬取
printUnivList(u_info, num=30) # 打印输出30个信息

在这里插入图片描述
XPath即为XML路径语言(XML Path Language),它是一种用来确定XML文档中某部分位置的语言。在XPath中,有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释以及文档(根)节点。XML文档是被作为节点树来对待的。

爬取丁香园-用户名和回复内容

from lxml import etree
import requests

#1. 获取url的html
url = "http://www.dxy.cn/bbs/thread/626626#626626"
req = requests.get(url)
html = req.text

#2.lxml解析html
tree = etree.HTML(html) 

#3.利用Xpath表达式获取user和content(重点)
user = tree.xpath('//div[@class="auth"]/a/text()')
#print(user)
content = tree.xpath('//td[@class="postbody"]')

#4. 保存爬取的内容
results = []
for i in range(0, len(user)):
    # print(user[i].strip()+":"+content[i].xpath('string(.)').strip())
    # print("*"*80)
    # 因为回复内容中有换行等标签,所以需要用string()来获取数据
    results.append(user[i].strip() + ":  " + content[i].xpath('string(.)').strip())

#打印爬取的结果
for i,result in zip(range(0, len(user)),results):
    print("user"+ str(i+1) + "-" + result)
    print("*"*100)

在这里插入图片描述
典型的搜索和替换操作要求您提供与预期的搜索结果匹配的确切文本。虽然这种技术对于对静态文本执行简单搜索和替换任务可能已经足够了,但它缺乏灵活性,若采用这种方法搜索动态文本,即使不是不可能,至少也会变得很困难。

通过使用正则表达式,可以:

  • 测试字符串内的模式。
    例如,可以测试输入字符串,以查看字符串内是否出现电话号码模式或信用卡号码模式。这称为数据验证。
  • 替换文本。
    可以使用正则表达式来识别文档中的特定文本,完全删除该文本或者用其他文本替换它。
  • 基于模式匹配从字符串中提取子字符串。
    可以查找文档内或输入域内特定的文本。

使用正则表达式的优势是什么? 简洁

  • 正则表达式是用来简洁表达一组字符串的表达式
  • 正则表达式是一种通用的字符串表达框架
  • 正则表达式是一种针对字符串表达“简洁”和“特征”思想的工具
  • 正则表达式可以用来判断某字符串的特征归属

正则表达式在文本处理中十分常用:

  • 同时查找或替换一组字符串
  • 匹配字符串的全部或部分(主要)

淘宝商品比价定向爬虫
爬取思路:

  1. 提交商品搜索请求,循环获取页面
  2. 对于每个页面,提取商品名称和价格信息
  3. 将信息输出到屏幕上
import requests
import re

def getHTMLText(url):
    """
    请求获取html,(字符串)
    :param url: 爬取网址
    :return: 字符串
    """
     try:
        # 添加头信息,
       kv = { 'cookie': 'thw=cn; v=0; t=ab66dffdedcb481f77fd563809639584; cookie2=1f14e41c704ef58f8b66ff509d0d122e; _tb_token_=5e6bed8635536; cna=fGOnFZvieDECAXWIVi96eKju; unb=1864721683; sg=%E4%B8%8B3f; _l_g_=Ug%3D%3D; skt=83871ef3b7a49a0f; cookie1=BqeGegkL%2BLUif2jpoUcc6t6Ogy0RFtJuYXR4VHB7W0A%3D; csg=3f233d33; uc3=vt3=F8dBy3%2F50cpZbAursCI%3D&id2=UondEBnuqeCnfA%3D%3D&nk2=u%2F5wdRaOPk21wDx%2F&lg2=VFC%2FuZ9ayeYq2g%3D%3D; existShop=MTU2MjUyMzkyMw%3D%3D; tracknick=%5Cu4E36%5Cu541B%5Cu4E34%5Cu4E3F%5Cu5929%5Cu4E0B; lgc=%5Cu4E36%5Cu541B%5Cu4E34%5Cu4E3F%5Cu5929%5Cu4E0B; _cc_=WqG3DMC9EA%3D%3D; dnk=%5Cu4E36%5Cu541B%5Cu4E34%5Cu4E3F%5Cu5929%5Cu4E0B; _nk_=%5Cu4E36%5Cu541B%5Cu4E34%5Cu4E3F%5Cu5929%5Cu4E0B; cookie17=UondEBnuqeCnfA%3D%3D; tg=0; enc=2GbbFv3joWCJmxVZNFLPuxUUDA7QTpES2D5NF0D6T1EIvSUqKbx15CNrsn7nR9g%2Fz8gPUYbZEI95bhHG8M9pwA%3D%3D; hng=CN%7Czh-CN%7CCNY%7C156; mt=ci=32_1; alitrackid=www.taobao.com; lastalitrackid=www.taobao.com; swfstore=97213; x=e%3D1%26p%3D*%26s%3D0%26c%3D0%26f%3D0%26g%3D0%26t%3D0%26__ll%3D-1%26_ato%3D0; uc1=cookie16=UtASsssmPlP%2Ff1IHDsDaPRu%2BPw%3D%3D&cookie21=UIHiLt3xThH8t7YQouiW&cookie15=URm48syIIVrSKA%3D%3D&existShop=false&pas=0&cookie14=UoTaGqj%2FcX1yKw%3D%3D&tag=8&lng=zh_CN; JSESSIONID=A502D8EDDCE7B58F15F170380A767027; isg=BMnJJFqj8FrUHowu4yKyNXcd2PXjvpa98f4aQWs-RbDvsunEs2bNGLfj8BYE6lWA; l=cBTDZx2mqxnxDRr0BOCanurza77OSIRYYuPzaNbMi_5dd6T114_OkmrjfF96VjWdO2LB4G2npwJ9-etkZ1QoqpJRWkvP.; whl=-1%260%260%261562528831082', 'user-agent': 'Mozilla/5.0'}
       r = requests.get(url, timeout=30, headers=kv)
        # r = requests.get(url, timeout=30)
        # print(r.status_code)
        r.raise_for_status()
        r.encoding = r.apparent_encoding
        return r.text
    except:
        return "爬取失败"

def parsePage(glist, html):
    '''
    解析网页,搜索需要的信息
    :param glist: 列表作为存储容器
    :param html: 由getHTMLText()得到的
    :return: 商品信息的列表
    '''
    try:
        # 使用正则表达式提取信息
        price_list = re.findall(r'\"view_price\"\:\"[\d\.]*\"', html)
        name_list = re.findall(r'\"raw_title\"\:\".*?\"', html)
        for i in range(len(price_list)):
            price = eval(price_list[i].split(":")[1])  #eval()在此可以去掉""
            name = eval(name_list[i].split(":")[1])
            glist.append([price, name])
    except:
        print("解析失败")

def printGoodList(glist):
    tplt = "{0:^4}\t{1:^6}\t{2:^10}"
    print(tplt.format("序号", "商品价格", "商品名称"))
    count = 0
    for g in glist:
        count = count + 1
        print(tplt.format(count, g[0], g[1]))

# 根据页面url的变化寻找规律,构建爬取url
goods_name = "书包"  # 搜索商品类型
start_url = "https://s.taobao.com/search?q=" + goods_name
info_list = []
page = 3  # 爬取页面数量

count = 0
for i in range(page):
    count += 1
    try:
        url = start_url + "&s=" + str(44 * i)
        html = getHTMLText(url)  # 爬取url
        parsePage(info_list, html) #解析HTML和爬取内容
        print("\r爬取页面当前进度: {:.2f}%".format(count * 100 / page), end="")  # 显示进度条
    except:
        continue

printGoodList(info_list)

在这里插入图片描述

三、ip代理、selenium、session和cookie的应用

3.1 ip代理

为什么会出现IP被封:网站为了防止被爬取,会有反爬机制,对于同一个IP地址的大量同类型的访问,会封锁IP,过一段时间后,才能继续访问。

如何应对IP被封的问题:

  1. 修改请求头,模拟浏览器(而不是代码去直接访问)
  2. 去访问采用代理IP并轮换
  3. 设置访问时间间隔

如何获取代理IP地址:

  • 从该网站获取: https://www.xicidaili.com/
  • inspect -> 鼠标定位:
  • 要获取的代理IP地址,属于class = "odd"标签的内容:代码如下,获取的代理IP保存在proxy_ip_list列表中
from bs4 import BeautifulSoup
import requests
import re
import json

#获取到代理IP地址后,发现数据缺失很多,再仔细查看elements,发现有些并非class = “odd”,而是…,这些数据没有被获取 class = "odd"奇数的结果,而没有class = "odd"的是偶数的结果通过bs4的find_all(‘tr’)来获取所有IP:
def open_proxy_url(url):
    user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36'
    headers = {'User-Agent': user_agent}
    try:
        r = requests.get(url, headers = headers, timeout = 10)
        r.raise_for_status()
        r.encoding = r.apparent_encoding
        return r.text
    except:
        print('无法访问网页' + url)

def get_proxy_ip(response):
    proxy_ip_list = []
    soup = BeautifulSoup(response, 'html.parser')
    proxy_ips = soup.find(id = 'ip_list').find_all('tr')
    for proxy_ip in proxy_ips:
        if len(proxy_ip.select('td')) >=8:
            ip = proxy_ip.select('td')[1].text
            port = proxy_ip.select('td')[2].text
            protocol = proxy_ip.select('td')[5].text
            if protocol in ('HTTP','HTTPS','http','https'):
                proxy_ip_list.append(f'{protocol}://{ip}:{port}')
    return proxy_ip_list

#使用代理:proxies的格式是一个字典:{‘http’: ‘http://IP:port‘,‘https’:'https://IP:port‘}把它直接传入requests的get方法中即可,web_data = requests.get(url, headers=headers, proxies=proxies)
def open_url_using_proxy(url, proxy):
    user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36'
    headers = {'User-Agent': user_agent}
    proxies = {}
    if proxy.startswith(('HTTPS','https')):
        proxies['https'] = proxy
    else:
        proxies['http'] = proxy
      try:
        r = requests.get(url, headers = headers, proxies = proxies, timeout = 10)
        r.raise_for_status()
        r.encoding = r.apparent_encoding
        return (r.text, r.status_code)
    except:
        print('无法访问网页' + url)
        print('无效代理IP: ' + proxy)
        return False

# 确认代理有效性:无论是免费还是收费的代理网站,提供的代理IP都未必有效,我们应该验证一下,有效后,再放入我们的代理IP池中,以下通过几种方式:访问网站,得到的返回码是200真正的访问某些网站,获取title等,验证title与预计的相同访问某些可以提供被访问IP的网站,类似于“查询我的IP”的网站,查看返回的IP地址是什么验证返回码
def check_proxy_avaliability(proxy):
    url = 'http://www.baidu.com'
    result = open_url_using_proxy(url, proxy)
    VALID_PROXY = False
    if result:
        text, status_code = result
        if status_code == 200:
            r_title = re.findall('<title>.*</title>', text)
            if r_title:
                if r_title[0] == '<title>百度一下,你就知道</title>':
                    VALID_PROXY = True
        if VALID_PROXY:
            check_ip_url = 'https://jsonip.com/'
            try:
                text, status_code = open_url_using_proxy(check_ip_url, proxy)
            except:
                return
            print('有效代理IP: ' + proxy)
            with open('valid_proxy_ip.txt','a') as f:
                f.writelines(proxy)
            try:
                source_ip = json.loads(text).get('ip')
                print(f'源IP地址为:{source_ip}')
                print('='*40)
            except:
                print('返回的非json,无法解析')
                print(text)
    else:
        print('无效代理IP: ' + proxy)

if __name__ == '__main__':
    proxy_url = 'https://www.xicidaili.com/'
    proxy_ip_filename = 'proxy_ip.txt'
    text = open(proxy_ip_filename, 'r').read()
    proxy_ip_list = get_proxy_ip(text)
    for proxy in proxy_ip_list:
        check_proxy_avaliability(proxy)

关于http和https代理:

  • 可以看到proxies中有两个键值对:{‘http’:
    ‘http://IP:port‘,‘https’:'https://IP:port‘}
  • 其中 HTTP 代理,只代理 HTTP 网站,对于 HTTPS 的网站不起作用,也就是说,用的是本机 IP,反之亦然。
  • 我刚才使用的验证的网站是https://jsonip.com,
    是HTTPS网站所以探测到的有效代理中,如果是https代理,则返回的是代理地址
  • 如果是http代理,将使用本机IP进行访问,返回的是我的公网IP地址

3.2、selenium

  • selenium是什么:一个自动化测试工具(大家都是这么说的)
  • selenium应用场景:用代码的方式去模拟浏览器操作过程(如:打开浏览器、在输入框里输入文字、回车等),在爬虫方面很有必要
  • 准备工作:1、安装selenium(pip install
    selenium)2、安装chromedriver(一个驱动程序,用以启动chrome浏览器,具体的驱动程序需要对应的驱动,在官网上可以找到下载地址)
from selenium import webdriver  # 启动浏览器需要用到
from selenium.webdriver.common.keys import Keys  # 提供键盘按键支持(最后一个K要大写)

#创建一个WebDriver实例:
driver = webdriver.Chrome("chromedriver驱动程序路径")

#打开一个页面:
driver.get("http://www.python.org")  # 这个时候chromedriver会打开一个Chrome浏览器窗口,显示的是网址所对应的页面

#关闭页面
driver.close()  # 关闭浏览器一个Tab
# or
driver.quit()  # 关闭浏览器窗口

高级-查找元素:在打开页面和关闭页面中间,就是各种操作!而查找元素这一点,和爬虫常见的HTML页面解析,定位到具体的某个元素基本一样,只不过,调用者是driver。

element = driver.find_element_by_name("q")  

高级-页面交互:找到元素后,就是进行“交互”,如键盘输入(需提前导入模块)。

element.send_keys(“some text”)  # 往一个可以输入对象中输入“some text”

element.send_keys(Keys.RETURN)  # 模拟键盘回车
#一般来说,这种方式输入后会一直存在,而要清空某个文本框中的文字,就需要:

element.clear()  # 清空element对象中的文字

高级-等待页面加载(wait):应用场景:含有ajax加载的page!因为在这种情况下,页面内的某个节点并不是在一开始就出现了,而在这种情况下,就不能“查找元素”,元素选择不到,就不好进行交互操作!等待页面加载这两个模块经常是一起导入的。

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

#显示等待:触发某个条件后才能够执行后续的代码
driver = webdriver.Firefox()
driver.get("http://somedomain/url_that_delays_loading")
try:    
    element = WebDriverWait(driver, 10).until(           
        EC.presence_of_element_located((By.ID, "myDynamicElement")))
finally:    
    driver.quit()
#其中,presence_of_element_located是条件,By.ID是通过什么方式来确认元素(这个是通过id),"myDynamicElement"这个就是某个元素的ID

#隐示等待:设置某个具体的等待时间
driver = webdriver.Firefox()
driver.implicitly_wait(10) # seconds
driver.get("http://somedomain/url_that_delays_loading")
myDynamicElement = driver.find_element_by_id("myDynami")

3.3、session和cookie

静态网页:
静态网页就是我们上一篇写的那种 html 页面,后缀为 .html 的这种文件,直接部署到或者是放到某个 web 容器上,就可以在浏览器通过链接直接访问到了,常用的 web 容器有 Nginx 、 Apache 、 Tomcat 、Weblogic 、 Jboss 、 Resin 等等,很多很多。
举个例子:https://desmonday.github.io/,就是静态网页的代表,这种网页的内容是通过纯粹的 HTML 代码来书写,包括一些资源文件:图片、视频等内容的引入都是使用 HTML 标签来完成的。它的好处当然是加载速度快,编写简单,访问的时候对 web 容器基本上不会产生什么压力。但是缺点也很明显,可维护性比较差,不能根据参数动态的显示内容等等。有需求就会有发展么,这时动态网页就应运而生了。

动态网页:

  • 大家常用的某宝、某东、拼夕夕等网站都是由动态网页组成的。
  • 动态网页可以解析 URL
    中的参数,或者是关联数据库中的数据,显示不同的网页内容。现在各位同学访问的网站大多数都是动态网站,它们不再简简单单是由 HTML
    堆砌而成,可能是由 JSP 、 PHP 等语言编写的,当然,现在很多由前端框架编写而成的网页小编这里也归属为动态网页。
  • 说到动态网页,各位同学可能使用频率最高的一个功能是登录,像各种电商类网站,肯定是登录了以后才能下单买东西。那么,问题来了,后面的服务端是如何知道当前这个人已经登录了呢?

HTTP1.0的特点是无状态无链接的
在这里插入图片描述

无状态就是指 HTTP 协议对于请求的发送处理是没有记忆功能的,也就是说每次 HTTP 请求到达服务端,服务端都不知道当前的客户端(浏览器)到底是一个什么状态。客户端向服务端发送请求后,服务端处理这个请求,然后将内容响应回客户端,完成一次交互,这个过程是完全相互独立的,服务端不会记录前后的状态变化,也就是缺少状态记录。这就产生了上面的问题,服务端如何知道当前在浏览器面前操作的这个人是谁?其实,在用户做登录操作的时候,服务端会下发一个类似于 token 凭证的东西返回至客户端(浏览器),有了这个凭证,才能保持登录状态。那么这个凭证是什么?

session和cookies
Session 是会话的意思,会话是产生在服务端的,用来保存当前用户的会话信息,而 Cookies 是保存在客户端(浏览器),有了 Cookie 以后,客户端(浏览器)再次访问服务端的时候,会将这个 Cookie 带上,这时,服务端可以通过 Cookie 来识别本次请求到底是谁在访问。可以简单理解为 Cookies 中保存了登录凭证,我们只要持有这个凭证,就可以在服务端保持一个登录状态。在爬虫中,有时候遇到需要登录才能访问的网页,只需要在登录后获取了 Cookies ,在下次访问的时候将登录后获取到的 Cookies 放在请求头中,这时,服务端就会认为我们的爬虫是一个正常登录用户。

实例:模拟登陆163

import time
from selenium import webdriver
from selenium.webdriver.common.by import By

"""
使用selenium进行模拟登陆
1.初始化ChromDriver
2.打开163登陆页面
3.找到用户名的输入框,输入用户名
4.找到密码框,输入密码
5.提交用户信息
"""

name = '*'
passwd = '*'
driver = webdriver.Chrome('./chromedriver')
driver.get('https://mail.163.com/')
# 将窗口调整最大
driver.maximize_window()
# 休息5s
time.sleep(5)
current_window_1 = driver.current_window_handle
print(current_window_1)

button = driver.find_element_by_id('lbNormal')
button.click()

driver.switch_to.frame(driver.find_element_by_xpath("//iframe[starts-with(@id, 'x-URS-iframe')]"))

email = driver.find_element_by_name('email')
#email = driver.find_element_by_xpath('//input[@name="email"]')
email.send_keys(name)
password = driver.find_element_by_name('password')
#password = driver.find_element_by_xpath("//input[@name='password']")
password.send_keys(passwd)
submit = driver.find_element_by_id("dologin")
time.sleep(15)
submit.click()
time.sleep(10)
print(driver.page_source)
driver.quit()

模拟登录丁香园,并抓取论坛页面所有的人员基本信息与回复帖子内容。

import requests, json, re, random,time
from bs4 import BeautifulSoup
from selenium import webdriver
from lxml import etree

class getUrl(object):
 """docstring for getUrl"""	
def __init__(self):
  self.headers={
            "Connection": "keep-alive",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 "  
                          "(KHTML, like Gecko) Chrome/51.0.2704.63 Safari/537.36",
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
            "Accept-Encoding": "gzip, deflate, sdch",
            "Accept-Language": "zh-CN,zh;q=0.8"
        };
  def run(self):
  browser = webdriver.Chrome('/home/ach/桌面/爬虫/学习任务/chromedriver')
  browser.get('https://auth.dxy.cn/accounts/login?service=http://www.dxy.cn/bbs/index.html')
  time.sleep(1)
  #切换账号密码登录表单
  js1 = 'document.querySelector("#j_loginTab1").style.display="none";'
  browser.execute_script(js1)
  time.sleep(1)
  js2 = 'document.querySelector("#j_loginTab2").style.display="block";'
  browser.execute_script(js2)
  #输入账号密码
  input_name = browser.find_element_by_name('username')
  input_name.clear()
  input_name.send_keys('*')# 这里为自己账号和密码
  input_pass = browser.find_element_by_name('password')
  input_pass.clear()
  input_pass.send_keys('*')
  browser.find_element_by_xpath('//*[@class="form__button"]/button').click()
  #此步骤应该有验证码,先跳过
  time.sleep(10)
  cookie = browser.get_cookies()
  cookie_dict = {i['name']:i['value'] for i in cookie}
  #转到抓取页面
  browser.get("http://www.dxy.cn/bbs/thread/626626#626626");
  html = browser.page_source
  tree = etree.HTML(html)
  user = tree.xpath('//div[@id="postcontainer"]//div[@class="auth"]/a/text()')
  content = tree.xpath('//td[@class="postbody"]')
  for i in range(0,len(user)):
      result = user[i].strip()+":"+content[i].xpath('string(.)').strip()
      #写入文件
      dir_file = open("DXY_records.txt",'a', encoding="utf-8")
      dir_file.write(result+"\n")
      dir_file.write('*' * 80+"\n")
      dir_file.close()
      print('*' * 5 +"抓取结束"+'*' * 5)

if __name__ == '__main__':
 geturl = getUrl()
 geturl.run()

四、腾讯新闻爬取

要点:

  • 了解ajax加载
  • 通过chrome的开发者工具,监控网络请求,并分析
  • 用selenium完成爬虫
import time
from  selenium import webdriver
from bs4 import BeautifulSoup
import pandas as pd

driver=webdriver.Chrome(executable_path="D:\chromedriver\chromedriver.exe")
driver.get("https://news.qq.com")

#了解ajax加载
for i in range(1,100):
    time.sleep(2)
    driver.execute_script("window.scrollTo(window.scrollX, %d);"%(i*200))
    
html=driver.page_source
#解析html
bsObj=BeautifulSoup(html,"lxml")


jxtits=bsObj.find_all("div",{"class":"jx-tit"})[0].find_next_sibling().find_all("li")

print("index",",","title",",","url")
for i,jxtit in enumerate(jxtits):
#     print(jxtit)
    
    try:
        text=jxtit.find_all("img")[0]["alt"]
    except:
        text=jxtit.find_all("div",{"class":"lazyload-placeholder"})[0].text
    try:
        url=jxtit.find_all("a")[0]["href"]
    except:
        print(jxtit)
    print(i+1,",",text,",",url)

csv_file = pd.DataFrame()
csv_file['index'] = csvRow_index
csv_file['title'] = csvRow_title
csv_file['url'] = csvRow_url
csv_file.to_csv('csv_file.csv',index=None)

爬取结果
在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值