python 爬虫开发从入门到实践 读书笔记(一)

Posted on Posted in python

正则表达式

正则表达式的基本符号

  • 点号 "." 代替除了换行符以外的任何一个字符
  • 星号 "*" 表示前面的一个子表达式(普通字符/另一个或几个正则表达式符号 0无限次
  • 问号 "?" 表示前面的子表达式01
  • 反斜杠 "" 不单独使用
    • 转义
    • \n 换行符
    • \t 制表符
    • \ 普通反斜杠
    • ' 单引号
    • " 双引号
    • \d 数字
  • 小括号 () 把括号里的内容提取出来

使用正则表达式

findall

返回所有满足要求的字符串

格式

re.findall(正则表达式, 原来的字符串, flags=0)

结果是一个列表, 包含了所有匹配的结果.如果没有匹配到,就是空列表

import re

content = "账号A密码:12345, 账号B密码:67890, 所有密码"
password_list = re.findall(':(.*?),', content)
print(password_list)
account_password = re.findall("账号(.*?)密码:(.*?),", content)
print(account_password)
==>
['12345', '67890']
[('A', '12345'), ('B', '67890')]

flags 参数一些辅助功能,例如忽略大小写,re.S 忽略换行符.

import re

html_string = '''
账号A密码是123
456,
'''
password_no_flag = re.findall('密码是(.*?),', html_string)
password_flag = re.findall('密码是(.*?),', html_string, re.S)
print(password_no_flag)
print(password_flag)

search

返回第一个满足要求的字符串

格式

re.search(正则表达式, 原来的字符串, flags=0)

结果是一个正则表达式的对象, 匹配不到就是 None. 如果需要得到匹配的结果, 通过 .group() 获取值

import re

content = "账号A密码:12345, 账号B密码:67890, 所有密码"
password_search = re.search("账号(.*?)密码:(.*?),", content)
print(password_search)
print(password_search.group())
print(password_search.group(0))
print(password_search.group(1)) # 对应上面第一个括号
print(password_search.group(2)) # 对应上面第二个括号

(.*?)

贪婪匹配

获得最短的能满足条件的表达式

括号内可以有其他字符, 比如 (20178*?)

python 文件操作

格式

with open('文件路径', '文件操作方式', encoding='utf-8') as f:
        对文件进行操作

使用 Python 读文件

文件路径是绝对路径, 也可以是相对路径. 但是不能使用 "~"家目录这种形式, 但是可以转化

import os
real_path = os.path.expanduser('~/project/xxx')

读文件

with open('text.txt', encoding='utf-8') as f:
    content_list = f.readlines() # readlines 按照列表返回
    print(content_list)
with open('text.txt', encoding='utf-8') as f:
    content_str = f.read() # read 返回一个整体字符串
    print(content_str)

使用 Python 写文件

with open("text.txt", "w", encoding="utf-8") as f: # w 新生成一个文件, wa 追加一个文件
    f.write("你好") # 写一大段字符串
    f.write("\n") # 写到文本的文件不会自动换行
    f.writelines(["第一\n", "第二"]) # 写入一个列表
    f.write("\n")
    f.write("end")

使用 Python 读/写 csv 文件

写 csv 文件

可以把字典写成 csv 文件, 或者把包含字典的列表写成 csv 文件, 列名和字典的 key 一一对应

import csv

data = [{"name": "user01", "age": 10},
        {"name": "user02", "age": 20},
        {"name": "user03", "age": 30}]

with open("new.csv", "w", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=['name', 'age'])
    writer.writeheader() # 表头
    writer.writerows(data) # 数据
    writer.writerow({"name": "user04", "age": 40})

读 csv 文件

第一种方法, 直接读

with open('new.csv', encoding="utf-8") as f:
    reader = csv.DictReader(f)
    for row in reader:
        print(row) # 这是个字典

第二种发放,利用列表推导式

with open('new.csv', encoding="utf-8") as f:
    reader = [x for x in csv.DictReader(f)]

for row in reader:
    print(row)

Python 获取源代码

requests

GET

import requests

html = requests.get("https://baidu.com").content.decode()
print(html)

POST

import requests
data = {'k1': 'v1', 'k2': 'v2'}
html_formdata = requsts.post("url", data=data).content.decode()
html_formdata = requsts.post("url", json=data).content.decode() # requests 自动将字典转 json

结合正则

title = re.search('title>(.*?)<', html, re.S).group(1) # 提取标题
content_list = re.findall("p>(.*?)<", html, re.S) # 提取正文
content_str = '\n'.join(content_list) # 用换行符拼起来

多线程爬虫

多线程

from multiprocessing.dummy import Pool


def calc_power2(num):
    return num*num


pool = Pool(3) # Pool 实现线程池
origin_num = [x for x in range(10)]
result = pool.map(calc_power2, origin_num)
print(f"{result}")

=>

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

小说爬虫例子

import requests
import re
import os
from multiprocessing.dummy import Pool

url_start = "http://www.kanunu8.com/book3/6879/"
html_start = requests.get(url_start).content.decode("GBK")

toc_block = re.findall("正文(.*?)</tbody>", html_start, re.S)[0]
toc_url = re.findall('href="(.*?)"', toc_block, re.S)
toc_url_list = []
for url in toc_url:
    toc_url_list.append(url_start + url)
print(toc_url_list)
os.makedirs("动物农场", exist_ok=True)


def get_doc_content(url):
    print(url)
    chapter_html = requests.get(url).content.decode("GBK")
    chapter_name = re.search('size="4">(.*?)<', chapter_html, re.S).group(1)
    text_block = re.search('<p>(.*?)</p>', chapter_html, re.S).group(1)
    text_block = text_block.replace('<br />', '')
    with open(os.path.join("动物农场", chapter_name + '.txt'), 'w',
              encoding="utf-8") as f:
        f.write(text_block)


pool = Pool(8)
result = pool.map(get_doc_content, toc_url_list)

html 内容解析

xpath

pip install lxml

返回一个列表

语法

  • 获取文本 //标签1[@属性1="属性值1"]/标签2[@属性2="属性值2"]/.../text()
  • 获取属性值 //标签1[@属性1="属性值1"]/标签2[@属性2="属性值2"]/.../@属性n # a/@href
  • 以相同的字符串开头 //标签[starts-with(@属性名, "相同的开头部分")] # //div[starts-with(@id, "test")]/text()
  • 属性值包含相同字符串 //标签[starts-with(@属性名, "包含的字符串")]
  • 对 xpath 返回的对象再次执行 xpath , 子 xpath 开头不需要添加斜线, 直接标签
  • 使用 string(.) 提取文本
import lxml.html

selector = lxml.html.fromstring(html)
info = selector.xpath('xpath 语句')
info_list = info[0].xpath(ul/li/text())
info_string = info.xpath('string(.)')

Beautifull Soup4

pip install beautifullsoup4
from bs4 import BeautifullSoup

解析源代码

soup = BeautifulSoup("网页源代码", "解析器")
soup = BeautifulSoup("网页源代码", 'html.parser') # 两种解析器
soup = BeautifulSoup("网页源代码", "lxml")

from bs4 import BeautifullSoup
info = soup.find(class_="test")
all_content = info.find_all('li')
for li in all_content:
    print(li.string) # 通过 string 读出文本信息
  • find_all() 返回的是 BS 对象组成的列表, 如果没有, 返回空列表
  • find() 返回的是 BS 对象, 如果有多个符合条件的 html 标签, 则返回第1个对象, 如果找不到, 就返回 None

find() 与 find_all() 参数相同

find(name, attrs, recursive, text, **kwargs)

  • name html 的标签名, body, div, ul, li
  • attrs 一个字典, key 是属性名, value是属性值 attrs={'class': 'usefull'}
  • recursive 的值为 True 或 False. 当为 False , BS4 不会搜索子标签
  • text 可以是一个字符串或正则表达式, 用户搜索标签里的文本信息
content = soup.find_all(text=re.compile('文本信息'))
for each in content:
    print(each.string)
  • **kwarg 表示 k=v 形式的参数. k 是属性, v 是属性值. 有时候属性非常特殊, 可以省略标签
find_all('div', id='test')
find_all(class_='iamstrange')
content = soup.find_all(re.compile('iam')) # 支持正则匹配
for each in content:
     print(each.string)

Leave a Reply

Your email address will not be published. Required fields are marked *

one + 17 =