目录
爬虫,即网络爬虫,大家可以理解为在网络上爬行的一直蜘蛛,互联网就比作一张大网,而爬虫便是在这张网上爬来爬去的蜘蛛咯,如果它遇到资源,那么它就会抓取下来。想抓取什么?这个由你来控制它咯。 比如它在抓取一个网页,在这个网中他发现了一条道路,其实就是指向网页的超链接,那么它就可以爬到另一张网上来获取数据。这样,整个连在一起的大网对这之蜘蛛来说触手可及,分分钟爬下来不是事儿。
在用户浏览网页的过程中,我们可能会看到许多好看的图片,比如 百度图片-发现多彩世界 ,我们会看到几张的图片以及百度搜索框,这个过程其实就是用户输入网址之后,经过 DNS 服务器,找到服务器主机,向服务器发出一个请求,服务器经过解析之后,发送给用户的浏览器 HTML、JS、CSS 等文件,浏览器解析出来,用户便可以看到形形色色的图片了。 因此,用户看到的网页实质是由 HTML 代码构成的,爬虫爬来的便是这些内容,通过分析和过滤这些 HTML 代码,实现对图片、文字等资源的获取。
URL,即统一资源定位符,也就是我们说的网址,统一资源定位符是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的 URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。
URL 的格式由三部分组成: ①第一部分是协议 (或称为服务方式)。 ②第二部分是存有该资源的主机 IP 地址 (有时也包括端口号)。 ③第三部分是主机资源的具体地址,如目录和文件名等。
爬虫爬取数据时必须要有一个目标的 URL 才可以获取数据,因此,它是爬虫获取数据的基本依据,准确理解它的含义对爬虫学习有很大帮助。
学习 Python,当然少不了环境的配置,最初我用的是 Notepad++,不过发现它的提示功能实在是太弱了,于是,在 Windows 下我用了 PyCharm,在 Linux 下我用了 Eclipse for Python。
利用 pip 安装
1 | $ pip install requests |
或者利用 easy_install
1 | $ easy_install requests |
通过以上两种方法均可以完成安装。
首先我们引入一个小例子来感受一下
1 2 3 4 5 6 7 8 | import requests r = requests.get('http://cuiqingcai.com') print type(r) print r.status_code print r.encoding #print r.text print r.cookies |
以上代码我们请求了本站点的网址,然后打印出了返回结果的类型,状态码,编码方式,Cookies 等内容。 运行结果如下
1 2 3 4 | <class 'requests.models.Response'> 200 UTF-8 <RequestsCookieJar[]> |
怎样,是不是很方便。别急,更方便的在后面呢。
requests 库提供了 http 所有的基本请求方式。例如
1 2 3 4 5 | r = requests.post("http://httpbin.org/post") r = requests.put("http://httpbin.org/put") r = requests.delete("http://httpbin.org/delete") r = requests.head("http://httpbin.org/get") r = requests.options("http://httpbin.org/get") |
嗯,一句话搞定。
最基本的 GET 请求可以直接用 get 方法
1 | r = requests.get("http://httpbin.org/get") |
如果想要加参数,可以利用 params 参数
1 2 3 4 5 | import requests payload = {'key1': 'value1', 'key2': 'value2'} r = requests.get("http://httpbin.org/get", params=payload) print r.url |
运行结果
1 | http://httpbin.org/get?key2=value2&key1=value1 |
如果想请求 JSON 文件,可以利用 json () 方法解析 例如自己写一个 JSON 文件命名为 a.json,内容如下
1 2 3 | ["foo", "bar", { "foo": "bar" }] |
利用如下程序请求并解析
1 2 3 4 5 | import requests r = requests.get("a.json") print r.text print r.json() |
运行结果如下,其中一个是直接输出内容,另外一个方法是利用 json () 方法解析,感受下它们的不同
1 2 3 4 | ["foo", "bar", { "foo": "bar" }] [u'foo', u'bar', {u'foo': u'bar'}] |
如果想获取来自服务器的原始套接字响应,可以取得 r.raw 。 不过需要在初始请求中设置 stream=True 。
1 2 3 4 5 | r = requests.get('https://github.com/timeline.json', stream=True) r.raw <requests.packages.urllib3.response.HTTPResponse object at 0x101194810> r.raw.read(10) '\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03' |
这样就获取了网页原始套接字内容。 如果想添加 headers,可以传 headers 参数
1 2 3 4 5 6 | import requests payload = {'key1': 'value1', 'key2': 'value2'} headers = {'content-type': 'application/json'} r = requests.get("http://httpbin.org/get", params=payload, headers=headers) print r.url |
通过 headers 参数可以增加请求头中的 headers 信息
对于 POST 请求来说,我们一般需要为它增加一些参数。那么最基本的传参方法可以利用 data 这个参数。
1 2 3 4 5 | import requests payload = {'key1': 'value1', 'key2': 'value2'} r = requests.post("http://httpbin.org/post", data=payload) print r.text |
运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | { "args": {}, "data": "", "files": {}, "form": { "key1": "value1", "key2": "value2" }, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Content-Length": "23", "Content-Type": "application/x-www-form-urlencoded", "Host": "httpbin.org", "User-Agent": "python-requests/2.9.1" }, "json": null, "url": "http://httpbin.org/post" } |
可以看到参数传成功了,然后服务器返回了我们传的数据。 有时候我们需要传送的信息不是表单形式的,需要我们传 JSON 格式的数据过去,所以我们可以用 json.dumps () 方法把表单数据序列化。
1 2 3 4 5 6 7 | import json import requests url = 'http://httpbin.org/post' payload = {'some': 'data'} r = requests.post(url, data=json.dumps(payload)) print r.text |
运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | { "args": {}, "data": "{\"some\": \"data\"}", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Content-Length": "16", "Host": "httpbin.org", "User-Agent": "python-requests/2.9.1" }, "json": { "some": "data" }, "url": "http://httpbin.org/post" } |
通过上述方法,我们可以 POST JSON 格式的数据 如果想要上传文件,那么直接用 file 参数即可 新建一个 a.txt 的文件,内容写上 Hello World!
1 2 3 4 5 6 | import requests url = 'http://httpbin.org/post' files = {'file': open('test.txt', 'rb')} r = requests.post(url, files=files) print r.text |
可以看到运行结果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | { "args": {}, "data": "", "files": { "file": "Hello World!" }, "form": {}, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Content-Length": "156", "Content-Type": "multipart/form-data; boundary=7d8eb5ff99a04c11bb3e862ce78d7000", "Host": "httpbin.org", "User-Agent": "python-requests/2.9.1" }, "json": null, "url": "http://httpbin.org/post" } |
这样我们便成功完成了一个文件的上传。 requests 是支持流式上传的,这允许你发送大的数据流或文件而无需先把它们读入内存。要使用流式上传,仅需为你的请求体提供一个类文件对象即可
1 2 | with open('massive-body') as f: requests.post('http://some.url/streamed', data=f) |
这是一个非常实用方便的功能。
如果一个响应中包含了 cookie,那么我们可以利用 cookies 变量来拿到
1 2 3 4 5 6 | import requests url = 'http://example.com' r = requests.get(url) print r.cookies print r.cookies['example_cookie_name'] |
以上程序仅是样例,可以用 cookies 变量来得到站点的 cookies 另外可以利用 cookies 变量来向服务器发送 cookies 信息
1 2 3 4 5 6 | import requests url = 'http://httpbin.org/cookies' cookies = dict(cookies_are='working') r = requests.get(url, cookies=cookies) print r.text |
运行结果
1 | '{"cookies": {"cookies_are": "working"}}' |
可以已经成功向服务器发送了 cookies
可以利用 timeout 变量来配置最大请求时间
1 | requests.get('http://github.com', timeout=0.001) |
注:timeout 仅对连接过程有效,与响应体的下载无关。 也就是说,这个时间只限制请求的时间。即使返回的 response 包含很大内容,下载需要一定时间,然而这并没有什么卵用。
在以上的请求中,每次请求其实都相当于发起了一个新的请求。也就是相当于我们每个请求都用了不同的浏览器单独打开的效果。也就是它并不是指的一个会话,即使请求的是同一个网址。比如
1 2 3 4 5 | import requests requests.get('http://httpbin.org/cookies/set/sessioncookie/123456789') r = requests.get("http://httpbin.org/cookies") print(r.text) |
结果是
1 2 3 | { "cookies": {} } |
很明显,这不在一个会话中,无法获取 cookies,那么在一些站点中,我们需要保持一个持久的会话怎么办呢?就像用一个浏览器逛淘宝一样,在不同的选项卡之间跳转,这样其实就是建立了一个长久会话。 解决方案如下
1 2 3 4 5 6 | import requests s = requests.Session() s.get('http://httpbin.org/cookies/set/sessioncookie/123456789') r = s.get("http://httpbin.org/cookies") print(r.text) |
在这里我们请求了两次,一次是设置 cookies,一次是获得 cookies 运行结果
1 2 3 4 5 | { "cookies": { "sessioncookie": "123456789" } } |
发现可以成功获取到 cookies 了,这就是建立一个会话到作用。体会一下。 那么既然会话是一个全局的变量,那么我们肯定可以用来全局的配置了。
1 2 3 4 5 6 | import requests s = requests.Session() s.headers.update({'x-test': 'true'}) r = s.get('http://httpbin.org/headers', headers={'x-test2': 'true'}) print r.text |
通过 s.headers.update 方法设置了 headers 的变量。然后我们又在请求中设置了一个 headers,那么会出现什么结果? 很简单,两个变量都传送过去了。 运行结果
1 2 3 4 5 6 7 8 9 10 | { "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Host": "httpbin.org", "User-Agent": "python-requests/2.9.1", "X-Test": "true", "X-Test2": "true" } } |
如果 get 方法传的 headers 同样也是 x-test 呢?
1 | r = s.get('http://httpbin.org/headers', headers={'x-test': 'true'}) |
嗯,它会覆盖掉全局的配置
1 2 3 4 5 6 7 8 9 | { "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Host": "httpbin.org", "User-Agent": "python-requests/2.9.1", "X-Test": "true" } } |
那如果不想要全局配置中的一个变量了呢?很简单,设置为 None 即可
1 | r = s.get('http://httpbin.org/headers', headers={'x-test': None}) |
运行结果
1 2 3 4 5 6 7 8 | { "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Host": "httpbin.org", "User-Agent": "python-requests/2.9.1" } } |
嗯,以上就是 session 会话的基本用法
现在随处可见 https 开头的网站,Requests 可以为 HTTPS 请求验证 SSL 证书,就像 web 浏览器一样。要想检查某个主机的 SSL 证书,你可以使用 verify 参数 现在 12306 证书不是无效的嘛,来测试一下
1 2 3 4 | import requests r = requests.get('https://kyfw.12306.cn/otn/', verify=True) print r.text |
结果
1 | requests.exceptions.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:590) |
果真如此 来试下 github 的
1 2 3 4 | import requests r = requests.get('https://github.com', verify=True) print r.text |
嗯,正常请求,内容我就不输出了。 如果我们想跳过刚才 12306 的证书验证,把 verify 设置为 False 即可
1 2 3 4 | import requests r = requests.get('https://kyfw.12306.cn/otn/', verify=False) print r.text |
发现就可以正常请求了。在默认情况下 verify 是 True,所以如果需要的话,需要手动设置下这个变量。
如果需要使用代理,你可以通过为任意请求方法提供 proxies 参数来配置单个请求
1 2 3 4 5 6 7 | import requests proxies = { "https": "http://41.118.132.69:4433" } r = requests.post("http://httpbin.org/post", proxies=proxies) print r.text |
也可以通过环境变量 HTTP_PROXY 和 HTTPS_PROXY 来配置代理
1 2 | export HTTP_PROXY="http://10.10.1.10:3128" export HTTPS_PROXY="http://10.10.1.10:1080" |
- import csv
- import pymysql
- import time
- import pandas as pd
- import re
- from bs4 import BeautifulSoup
- from selenium import webdriver
- from selenium.webdriver.chrome.service import Service
- from selenium.webdriver import ActionChains
- from selenium.webdriver.common.by import By
-
- #连接数据库,将csv文件保存至mysql中
- def getConnect():
-
- # 连接MySQL数据库(注意:charset参数是utf8而不是utf-8)
- conn = pymysql.connect(host='localhost',port=3307, user='root', password='liziyi123456', db='db_douban', charset='utf8')
- # 创建游标对象
- cursor = conn.cursor()
- # 读取csv文件
- with open('mm.csv', 'r', encoding='utf-8') as f:
- read = csv.reader(f)
-
- # 一行一行地存,除去第一行
- for each in list(read)[1:]:
- i = tuple(each)
- # 使用SQL语句添加数据
- sql = "INSERT INTO movices VALUES" + str(i) # movices是表的名称
- cursor.execute(sql) # 执行SQL语句
-
- conn.commit() # 提交数据
- cursor.close() # 关闭游标
- conn.close() # 关闭数据库
-
- def getMovice(year):
- server = Service('chromedriver.exe')
- driver = webdriver.Chrome(service=server)
- driver.implicitly_wait(60)
- driver.get( "https://movie.douban.com/tag/#/?sort=S&range=0,10&tags="+year+",%E7%94%B5%E5%BD%B1")
- driver.maximize_window()
- actions = ActionChains(driver)
- actions.scroll(0, 0, 0, 600).perform()
-
- for i in range(50): #循环50次,爬取1000部电影
- btn = driver.find_element(by=By.CLASS_NAME, value="more")
- time.sleep(3)
- actions.move_to_element(btn).click().perform()
- actions.scroll(0, 0, 0, 600).perform()
-
- html = driver.page_source
- driver.close()
- return html
-
- def getDetails(url): #获取每个电影的详情
- option = webdriver.ChromeOptions()
- option.add_argument('headless')
- driver = webdriver.Chrome(options=option)
- driver.get(url=url)
- html = driver.page_source
-
- soup = BeautifulSoup(html, 'lxml')
- div = soup.find('div', id='info')
- #spans = div.find_all('span')
- ls = div.text.split('\n')
- #print(ls)
- data = None;
- country = None;
- type = None;
- time = None;
- for p in ls:
- if re.match('类型: .*', p):
- type = p[4:].split(' / ')[0]
- elif re.match('制片国家/地区: .*', p):
- country = p[9:].split(' / ')[0]
- elif re.match('上映日期: .*', p):
- data = p[6:].split(' / ')[0]
- elif re.match('片长: .*', p):
- time = p[4:].split(' / ')[0]
-
- ls.clear()
-
- driver.quit()
-
- name = soup.find('h1').find('span').text
- score = soup.find('strong', class_='ll rating_num').text
- numOfRaters = soup.find('div', class_='rating_sum').find('span').text
-
- return {'name': name, 'data': data, 'country': country, 'type': type, 'time': time,
- 'score': score, 'numOfRaters': numOfRaters}
-
- def getNameAUrl(html, year):
- allM = []
- soup = BeautifulSoup(html, 'lxml')
- divs = soup.find_all('a', class_='item')
- for div in divs:
- url = div['href'] #获取网站链接
- i = url.find('?')
- url = url[0:i]
- name = div.find('img')['alt'] #获取电影名称
- allM.append({'电影名':name,'链接':url})
- pf = pd.DataFrame(allM,columns=['电影名','链接'])
- pf.to_csv("movice_"+year+".csv",encoding = 'utf-8',index=False)
-
- def getMovices(year):
- allM = []
- data = pd.read_csv('movice_'+year+'.csv', sep=',', header=0, names=['name', 'url'])
- i = int(0)
- for row in data.itertuples():
- allM.append(getDetails(getattr(row, 'url')))
- i += 1
- if i == 2:
- break
- print('第'+str(i)+'部成功写入')
- pf = pd.DataFrame(allM,columns=['name','data','country','type','time','score','numOfRaters'])
- pf.to_csv("mm.csv",encoding = 'utf-8',index=False,mode='a')
-
-
- if __name__ == '__main__':
-
- # 获取电影链接
- # htmll=getMovice('2022')
- # getNameAUrl(htmll,'2022')
- #获取电影的详细情况,保存至movice.csv中
-
- # a=getDetails("https://movie.douban.com/subject/33459931")
- # print(a)
- getMovices('2022')
- #getConnect()