学习日记-从爬虫到接口到APP
发布日期:2021-08-13 04:05:40 浏览次数:14 分类:技术文章

本文共 10856 字,大约阅读时间需要 36 分钟。

最近都在复习J2E,多学习一些东西肯定是好的,而且现在移动开发工作都不好找了,有工作就推荐一下小弟呗,广州佛山地区,谢谢了。

这篇博客要做的效果很简单,就是把我博客的第一页每个条目显示在APP上,条目包括标题、摘要和状态,如图:

 

所以这篇博客将会涉及:

  1. 数据库(MySql)简单设计(建表、插入数据)
  2. 简单爬虫(用Python爬取网页内容,写入数据库)
  3. 简单接口开发(Struts和Hibernate)
  4. APP网络请求(Retrofit、Gson、RxJava等)

大体的流程就是:先创建数据库,通过爬虫手段爬取博客首页的条目内容并填充至数据库,接着搭建简单的JavaWeb后台,提供接口访问,通过网络请求返回数据库中的数据。

 

① 数据库设计

要爬取数据和接口开发,肯定都是需要先创建数据库和数据表的。这里使用的是MySql,操作的工具是Navicat。对于上面的数据,我们需要建立对应的表:

其中id为主键且自增长。创建完毕可以进行插入和删除等测试。

 

② 爬取网页数据

静态网页的爬取是比较简单的,其实就是根据网页源码进行解析匹配,而Python的正则表达式较为强大,所以这里使用Python来进行操作,另外,基础的爬虫也可以使用一些库来简化操作,这里会用到request和bs4两个,request用于网络请求,而bs4则是用于解析网页源码得到我们想要的数据。最后,通过MySQLdb对数据进行存储。

先分析网页源码,可以使用Chrome来观察结构:

得到结构之后就可以进行编码:

文件名:MySpider.py

1 #coding=utf-8 2  3 import sys 4 import requests 5 from bs4 import BeautifulSoup 6 import MySQLdb 7  8 reload(sys) 9 sys.setdefaultencoding('utf-8')10 11 # 定义一个博客类12 class Blog:13     title = ""14     desc = ""15     postDate = ""16     status = ""17 18 # 进行网络请求拉取源码,地址为我博客首页19 response = requests.get("http://www.cnblogs.com/Fndroid/")20 # 使用BeautifulSoup进行处理21 soup = BeautifulSoup(response.text, "html.parser")22 23 blogs = []24 # 根据源码格式爬取栏目,找到class为day的标签,获取并遍历其子div25 for day in soup.findAll(class_='day'):26     divs = day.findAll("div")27     n = 028     b = Blog()29     for div in divs:30         if n == 0:31             # 爬取发表时间32             b.postDate = div.a.string33         elif n == 1:34             # 爬取标题35             b.title = div.a.string36         elif n == 2:37             # 爬取摘要38             b.desc = div.div.contents[0]39         elif n == 5:40             # 爬取文章状态41             b.status = div.contents[0]42         elif n == 6:43             n = 044             blogs.append(b)45             break46 47         n += 148 49 # 连接数据库,数据库用户名root,密码root,数据库名myblog,编码格式utf850 db = MySQLdb.connect("localhost", "root", "root", "myblog", charset="utf8")51 cursor = db.cursor()52 53 for bl in blogs:54     sub_sql = "'"+bl.title+"','"+bl.desc+"','"+bl.postDate+"','"+bl.status+"'"55     # 构造sql语句,插入数据56     sql = "insert into Blog(title,description,post_date,post_status) values("+sub_sql+")"57     try:58         cursor.execute(sql)59         db.commit()60     except:61         db.rollback()62 63 db.close()

主要的功能步骤已经在源码注释中标注了。接着运行程序,查看数据库内容如下则表示正确:

其中id值只要是自增长即可,可以不与上图对应。另外,如果APP需要点击条目跳转到博客内容,还需要把url获取下来,这里只是简单的事例就不拉了。

 

③ 接口开发

这个接口其实也很好理解,就是通过一个URL访问得到对应的数据,数据格式可以是JSON或者Xml,我们通过这些数据进行页面显示等等。而我们这里使用的是J2E中的Struts和Hibernate来搭建这个简单的后台。Struts用来拦截请求、Hibernate用于操作数据库。

实际上,Python也是可以做到的,但是目前国内很多中小企业都是用的J2E,所以......

环境搭建什么的就不说了,网上一搜一大堆,或者直接使用MyEclipse,快捷方便。

首先,编写对应于数据库的实体Bean:

1 public class Blog implements java.io.Serializable { 2  3     // Fields 4  5     private Integer id; 6     private String title; 7     private String description; 8     private String postDate; 9     private String postStatus;10 11     // Constructors12 13     /** default constructor */14     public Blog() {15     }16 17     /** full constructor */18     public Blog(String title, String description, String postDate, String postStatus) {19         this.title = title;20         this.description = description;21         this.postDate = postDate;22         this.postStatus = postStatus;23     }24 25     // Property accessors26 27     public Integer getId() {28         return this.id;29     }30 31     public void setId(Integer id) {32         this.id = id;33     }34 35     public String getTitle() {36         return this.title;37     }38 39     public void setTitle(String title) {40         this.title = title;41     }42 43     public String getDescription() {44         return this.description;45     }46 47     public void setDescription(String description) {48         this.description = description;49     }50 51     public String getPostDate() {52         return this.postDate;53     }54 55     public void setPostDate(String postDate) {56         this.postDate = postDate;57     }58 59     public String getPostStatus() {60         return this.postStatus;61     }62 63     public void setPostStatus(String postStatus) {64         this.postStatus = postStatus;65     }66 67 }

接着在对应包中编写一个Blog.hbn.xml文件,用于Hibernate数据映射:

1 
2 4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

如果你使用MyEclipse,则这些文件可以通过自带的MyEclipse Hibernate工具生成。

接着,创建一个Dao来获取数据库内容:

1 public class BlogDao { 2  3     public List
getBlogs() { 4 Configuration conf = new Configuration().configure(); 5 SessionFactory sessionFactory = conf.buildSessionFactory(); 6 Session session = sessionFactory.openSession(); 7 Query query = session.createQuery("from Blog"); 8 List
list = query.list(); 9 return list;10 }11 }

最后创建并配置一个Action来拦截请求,并填充数据,这里使用Gson来进行数据包装,所以要记得导入Gson的jar包:

1 public class BooksAction extends ActionSupport { 2      3     @Override 4     public String execute() throws Exception { 5         BlogDao dao = new BlogDao(); 6         List
blogs = dao.getBlogs(); 7 String result = createJsonString(!blogs.isEmpty(), blogs); 8 HttpServletRequest request = ServletActionContext.getRequest(); 9 // 将数据填充至内置对象request中,这样在jsp中可以获取得到10 request.setAttribute("json", createJsonString(!blogs.isEmpty(), blogs));11 return "success";12 }13 14 /**15 * 通过数据集生成JSON格式的数据16 * @param res 数据集是否为空17 * @param blogs 数据集18 * @return19 */20 private String createJsonString(boolean res, List
blogs) {21 JsonObject resultJson = new JsonObject();22 JsonArray array = new JsonArray();23 resultJson.addProperty("result", res? 1:0);24 resultJson.addProperty("err_msg", res? "服务器成功返回数据":"服务器错误");25 if (res){26 for (Blog blog : blogs) {27 JsonObject bObject = new JsonObject();28 bObject.addProperty("id", blog.getId());29 bObject.addProperty("title", blog.getTitle());30 bObject.addProperty("desc", blog.getDescription());31 bObject.addProperty("post_date", blog.getPostDate());32 bObject.addProperty("status", blog.getPostStatus());33 array.add(bObject);34 }35 }36 resultJson.add("blogs", array);37 return resultJson.toString();38 }39 }

配置Struts.xml:

success.jsp

success.jsp

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%><%=request.getAttribute("json") %>

直接显示request内置对象中对应的json格式值即可。

接口已经编写完毕,接着启动服务器,并且布置项目,这个时候可以用浏览器访问http://localhost:8080/WebDemo/listBlogs来看是否成功,其中WebDemo为项目名称,listBlogs为Action名:

浏览器效果:

这肯定是不够直观的,所以我们可以尝试一下一些用于开发的请求分析工具,例如Postman(Chrome应用商店下载):

这样就可以看到对应的格式。

 

④ APP编写

万事俱备,只欠东风了。APP的内容也不多,通过一个RecyclerView显示每个条目的标题、摘要和状态即可。

初始化使用空列表构造一个RecyclerView,接着通过RxJava和Retrofit进行网络请求,得到数据传递给数据列表并刷新界面。

注意:以下代码可能会出现令人身体不适的Lumbda表达式

主界面布局代码省略,里面只有一个RecyclerView。

编写RecyclerView每个Item的布局(使用数据绑定):

1 
2
3 4
5 6
9
10 11
17 18
24 25
30 31
35 36 37 38

创建Recycler的Adapter:

1 public class MyAdapter extends RecyclerView.Adapter { 2     private Context mContext; 3     private ArrayList
mBlogsArrayList; 4 public MyAdapter(Context context, ArrayList
blogsArrayList) { 5 this.mContext = context; 6 this.mBlogsArrayList = blogsArrayList; 7 } 8 9 @Override10 public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {11 // 获取绑定实例,并存储在ViewHolder中12 ItemBlogBinding binding = DataBindingUtil.inflate(LayoutInflater.from(mContext), R.layout13 .item_blog, parent, false);14 VH vh = new VH(binding.getRoot());15 vh.binding = binding;16 return vh;17 }18 19 @Override20 public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {21 Blogs.BlogsBean blogsBean = mBlogsArrayList.get(position);22 VH vh = (VH) holder;23 // 设置数据绑定数据源24 vh.binding.setVariable(com.fndroid.retrofitdemo.BR.blog, blogsBean);25 }26 27 @Override28 public int getItemCount() {29 return mBlogsArrayList.size();30 }31 32 class VH extends RecyclerView.ViewHolder{33 ItemBlogBinding binding;34 public VH(View itemView) {35 super(itemView);36 }37 }38 }

编写Retrofit的请求服务:

public interface IdentifyService{    @GET("listBlogs")    public Observable
getBlogs();}

最后编写Activity的内容:

1 public class MainActivity extends AppCompatActivity { 2     private static final String TAG = "MainActivity"; 3  4     // 这里不能写localhost,因为模拟器和服务器ip不同 5     private static final String URL = "http:192.168.1.181:8080/WebDemo/"; 6     private ArrayList
mBlogsArrayList; 7 private MyAdapter mMyAdapter; 8 9 @BindView(R.id.main_rv)10 RecyclerView mRecyclerView;11 12 @Override13 protected void onCreate(Bundle savedInstanceState) {14 super.onCreate(savedInstanceState);15 setContentView(R.layout.activity_main);16 ButterKnife.bind(this);17 18 mRecyclerView.setLayoutManager(new LinearLayoutManager(this));19 // 传入空数据源20 mBlogsArrayList = new ArrayList<>();21 mMyAdapter = new MyAdapter(this, mBlogsArrayList);22 mRecyclerView.setAdapter(mMyAdapter);23 24 // 使用Gson解析数据,用RxJava2封装请求25 Retrofit ret = new Retrofit.Builder().baseUrl(URL).addConverterFactory26 (GsonConverterFactory.create()).addCallAdapterFactory(RxJava2CallAdapterFactory27 .create()).build();28 IdentifyService identifyService = ret.create(IdentifyService.class);29 Observable
blogs = identifyService.getBlogs();30 blogs.subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread())31 .subscribe(b -> {32 mBlogsArrayList.addAll(b.getBlogs());33 mMyAdapter.notifyDataSetChanged();34 });35 }36 }

因为使用了各种框架,所以内容也很简单,毕竟都2016年了,谁不用框架对吗。

这里对使用的各种框架做一个简单的说明:

  • RxJava2:异步请求必须要掌握的
  • Retrofit:它聪明的提供了Gson、RxJava2等支持,底层也是基于okhttp,所以性能也较好,也是必须掌握的
  • databinding(数据绑定):官方出品,免去setText、findViewById等冗余代码
  • RetroLumbda:在Java7上提供Lumbda语言支持,毕竟官方默认1.7,改为1.8会导致Instant Run失效,所以你懂的
  • Butterknife:不多说了吧这个

 

转载于:https://www.cnblogs.com/Fndroid/p/6046179.html

转载地址:https://blog.csdn.net/weixin_30675247/article/details/99001673 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:记录一个简单的HttpClient抓取页面内容
下一篇:39个让你受益的HTML5教程

发表评论

最新留言

做的很好,不错不错
[***.243.131.199]2024年04月08日 16时27分49秒