import requestsfrom bs4 import beautifulsoup headers = {'user-agent': 'mozilla/5.0 (windows nt 10.0; win64; x64) applewebkit/537.36 (khtml, like gecko)' ' chrome/52.0.2743.116 safari/537.36 edge/15.16193'} start_url = 'https://www.pythonscraping.com'r = requests.get(start_url, headers=headers) soup = beautifulsoup(r.text, 'lxml')# 获取所有img标签img_tags = soup.find_all('img')for tag in img_tags:print(tag['src'])
http://pythonscraping.com/img/lrg%20(1).jpg
将网页表格存储到csv文件中以这个网址为例,有好几个表格,我们对第一个表格进行爬取。wiki-各种编辑器的比较
import csvimport requestsfrom bs4 import beautifulsoup headers = {'user-agent': 'mozilla/5.0 (windows nt 10.0; win64; x64) applewebkit/537.36 (khtml, like gecko)' ' chrome/52.0.2743.116 safari/537.36 edge/15.16193'} url = 'https://en.wikipedia.org/wiki/comparison_of_text_editors'r = requests.get(url, headers=headers) soup = beautifulsoup(r.text, 'lxml')# 只要第一个表格rows = soup.find('table', class_='wikitable').find_all('tr')# csv写入时候每写一行会有一空行被写入,所以设置newline为空with open('editors.csv', 'w', newline='', encoding='utf-8') as f: writer = csv.writer(f)for row in rows: csv_row = []for cell in row.find_all(['th', 'td']): csv_row.append(cell.text) writer.writerow(csv_row)
需要注意的有一点,打开文件的时候需要指定newline='',因为写入csv文件时,每写入一行就会有一空行被写入。
从网络读取csv文件上面介绍了将网页内容存到csv文件中。如果是从网上获取到了csv文件呢?我们不希望下载后再从本地读取。但是网络请求的话,返回的是字符串而非文件对象。csv.reader()需要传入一个文件对象。故需要将获取到的字符串转换成文件对象。python的内置库,stringio和bytesio可以将字符串/字节当作文件一样来处理。对于csv模块,要求reader迭代器返回字符串类型,所以使用stringio,如果处理二进制数据,则用bytesio。转换为文件对象,就能用csv模块处理了。
下面的代码最为关键的就是data_file = stringio(csv_data.text)将字符串转换为类似文件的对象。
from io import stringioimport csvimport requests csv_data = requests.get('http://pythonscraping.com/files/montypythonalbums.csv') data_file = stringio(csv_data.text) reader = csv.reader(data_file)for row in reader:print(row)
['name', 'year'] ["monty python's flying circus", '1970'] ['another monty python record', '1971'] ["monty python's previous record", '1972'] ['the monty python matching tie and handkerchief', '1973'] ['monty python live at drury lane', '1974'] ['an album of the soundtrack of the trailer of the film of monty python and the holy grail', '1975'] ['monty python live at city center', '1977'] ['the monty python instant record collection', '1977'] ["monty python's life of brian", '1979'] ["monty python's cotractual obligation album", '1980'] ["monty python's the meaning of life", '1983'] ['the final rip off', '1987'] ['monty python sings', '1989'] ['the ultimate monty python rip off', '1994'] ['monty python sings again', '2014']
dictreader可以像操作字典那样获取数据,把表的第一行(一般是标头)作为key。可访问每一行中那个某个key对应的数据。
每一行数据都是orderdict,使用key可访问。看上面打印信息的第一行,说明由name和year两个key。也可以使用reader.fieldnames查看。
from io import stringioimport csvimport requests csv_data = requests.get('http://pythonscraping.com/files/montypythonalbums.csv') data_file = stringio(csv_data.text) reader = csv.dictreader(data_file)# 查看keyprint(reader.fieldnames)for row in reader:print(row['year'], row['name'], sep=': ')
['name', 'year'] 1970: monty python's flying circus 1971: another monty python record 1972: monty python's previous record 1973: the monty python matching tie and handkerchief 1974: monty python live at drury lane 1975: an album of the soundtrack of the trailer of the film of monty python and the holy grail 1977: monty python live at city center 1977: the monty python instant record collection 1979: monty python's life of brian 1980: monty python's cotractual obligation album 1983: monty python's the meaning of life 1987: the final rip off 1989: monty python sings 1994: the ultimate monty python rip off 2014: monty python sings again
存储数据
大数据存储与数据交互能力, 在新式的程序开发中已经是重中之重了.
存储媒体文件的2种主要方式: 只获取url链接, 或直接将源文件下载下来
直接引用url链接的优点:
爬虫运行得更快,耗费的流量更少,因为只要链接,不需要下载文件。
可以节省很多存储空间,因为只需要存储 url 链接就可以。
存储 url 的代码更容易写,也不需要实现文件下载代码。
不下载文件能够降低目标主机服务器的负载。
直接引用url链接的缺点:
这些内嵌在网站或应用中的外站 url 链接被称为盗链(hotlinking), 每个网站都会实施防盗链措施。
因为链接文件在别人的服务器上,所以应用就要跟着别人的节奏运行了。
盗链是很容易改变的。如果盗链图片放在博客上,要是被对方服务器发现,很可能被恶搞。如果 url 链接存起来准备以后再用,可能用的时候链接已经失效了,或者是变成了完全无关的内容。
python3的urllib.request.urlretrieve可以根据文件的url下载文件:
from urllib.request import urlretrievefrom urllib.request import urlopenfrom bs4 import beautifulsouphtml = urlopen("http://www.pythonscraping.com")bsobj = beautifulsoup(html)imagelocation = bsobj.find("a", {"id": "logo"}).find("img")["src"]urlretrieve (imagelocation, "logo.jpg")
csv(comma-separated values, 逗号分隔值)是存储表格数据的常用文件格式
网络数据采集的一个常用功能就是获取html表格并写入csv
除了用户定义的变量名,mysql是不区分大小写的, 习惯上mysql关键字用大写表示
连接与游标(connection/cursor)是数据库编程的2种模式:
连接模式除了要连接数据库之外, 还要发送数据库信息, 处理回滚操作, 创建游标对象等
一个连接可以创建多个游标, 一个游标跟踪一种状态信息, 比如数据库的使用状态. 游标还会包含最后一次查询执行的结果. 通过调用游标函数, 如fetchall获取查询结果
游标与连接使用完毕之后,务必要关闭, 否则会导致连接泄漏, 会一直消耗数据库资源
使用try ... finally语句保证数据库连接与游标的关闭
让数据库更高效的几种方法:
给每张表都增加id字段. 通常数据库很难智能地选择主键
用智能索引, create index definition on dictionary (id, definition(16));
选择合适的范式
发送email, 通过爬虫或api获取信息, 设置条件自动发送email! 那些订阅邮件, 肯定就是这么来的!
保存链接之间的联系比如链接a,能够在这个页面里找到链接b。则可以表示为a -> b。我们就是要保存这种联系到数据库。先建表:
pages表只保存链接url。
create table `pages` ( `id` int(11) not null auto_increment, `url` varchar(255) default null, `created` timestamp not null default current_timestamp, primary key (`id`) )
links表保存链接的fromid和toid,这两个id和pages里面的id是一致的。如1 -> 2就是pages里id为1的url页面里可以访问到id为2的url的意思。
create table `links` ( `id` int(11) not null auto_increment, `fromid` int(11) default null, `toid` int(11) default null, `created` timestamp not null default current_timestamp, primary key (`id`)
上面的建表语句看起来有点臃肿,我是先用可视化工具建表后,再用show create table pages这样的语句查看的。
import reimport pymysqlimport requestsfrom bs4 import beautifulsoup headers = {'user-agent': 'mozilla/5.0 (windows nt 10.0; win64; x64) applewebkit/537.36 (khtml, like gecko)' ' chrome/52.0.2743.116 safari/537.36 edge/15.16193'} conn = pymysql.connect(host='localhost', user='root', password='admin', db='wiki', charset='utf8') cur = conn.cursor()def insert_page_if_not_exists(url): cur.execute(f"select * from pages where url='{url}';")# 这条url没有插入的话if cur.rowcount == 0:# 那就插入cur.execute(f"insert into pages(url) values('{url}');") conn.commit()# 刚插入数据的idreturn cur.lastrowid# 否则已经存在这条数据,因为url一般是唯一的,所以获取一个就行,取脚标0是获得idelse:return cur.fetchone()[0]def insert_link(from_page, to_page):print(from_page, ' -> ', to_page) cur.execute(f"select * from links where fromid={from_page} and toid={to_page};")# 如果查询不到数据,则插入,插入需要两个pages的id,即两个urlif cur.rowcount == 0: cur.execute(f"insert into links(fromid, toid) values({from_page}, {to_page});") conn.commit()# 链接去重pages = set()# 得到所有链接def get_links(page_url, recursion_level):global pagesif recursion_level == 0:return# 这是刚插入的链接page_id = insert_page_if_not_exists(page_url) r = requests.get('https://en.wikipedia.org' + page_url, headers=headers) soup = beautifulsoup(r.text, 'lxml') link_tags = soup.find_all('a', href=re.compile('^/wiki/[^:/]*$'))for link_tag in link_tags:# page_id是刚插入的url,参数里再次调用了insert_page...方法,获得了刚插入的url里能去往的url列表# 由此形成联系,比如刚插入的id为1,id为1的url里能去往的id有2、3、4...,则形成1 -> 2, 1 -> 3这样的联系insert_link(page_id, insert_page_if_not_exists(link_tag['href']))if link_tag['href'] not in pages: new_page = link_tag['href'] pages.add(new_page)# 递归查找, 只能递归recursion_level次get_links(new_page, recursion_level - 1)if __name__ == '__main__':try: get_links('/wiki/kevin_bacon', 5)except exception as e:print(e)finally: cur.close() conn.close()
1 -> 2 2 -> 1 1 -> 2 1 -> 3 3 -> 4 4 -> 5 4 -> 6 4 -> 7 4 -> 8 4 -> 4 4 -> 4 4 -> 9 4 -> 9 3 -> 10 10 -> 11 10 -> 12 10 -> 13 10 -> 14 10 -> 15 10 -> 16 10 -> 17 10 -> 18 10 -> 19 10 -> 20 10 -> 21 ...
看打印的信息,一目了然。看前两行打印,pages表里id为1的url可以访问id为2的url,同时pages表里id为2的url可以访问id为1的url...依次类推。
首先需要使用insert_page_if_not_exists(page_url)获得链接的id,然后使用insert_link(fromid, toid)形成联系。fromid是当前页面的url,toid则是从当前页面能够去往的url的id,这些能去往的url用bs4找到以列表形式返回。当前所处的url即page_id,所以需要在insert_link的第二个参数中,再次调用insert_page_if_not_exists(link)以获得列表中每个url的id。由此形成了联系。比如刚插入的id为1,id为1的url里能去往的id有2、3、4...,则形成1 -> 2, 1 -> 3这样的联系。
看下数据库。下面是pages表,每一个id都对应一个url。
然后下面是links表,fromid和toid就是pages中的id。当然和打印的数据是一样的咯,不过打印了看看就过去了,存下来的话哪天需要分析这些数据就大有用处了。
以上就是python采集--数据的储存的详细内容。
