冲刺(二)
徐利峰:
今天利用爬虫去解析铁道大学新闻官网:http://xcb.stdu.edu.cn/2009-05-05-02-26-33.html
分析后发现其标题都是在tr,class="sectiontableentry1"和class=“sectiontableentry2”的里面,因此我就将这两种全部获取
获取后遍历存储他们的href(单个新闻的网址)
通过对单个网址进行分析,打算存入数据库里的信息有:标题,日期,点击次数,正文,图片链接
对于标题,日期,点击次数比较简单
难点在于:正文的爬取和图片链接
①爬取正文时:正文都在div.article-content下的p标签里,因此我们获取所有的p标签的集合
一开始我以为就是在p标签下的span里的是正文,爬取了10个后发现漏了一点东西,原来b标签里的也是正文里的内容
由于整篇文章是有p标签分割的,一个p标签代表这一段,因此我将爬取的所有p标签遍历,之后通过p的contents来访问他的子节点,根据子节点的name属性来判断是“span”还是“p”,爬取完一段后存储到正文集合里。
r=requests.get(url,headers=headers) content=r.content.decode('utf-8') soup = BeautifulSoup(content, 'html.parser') trs=soup.find_all('tr',class_='sectiontableentry1') trs+=soup.find_all('tr',class_='sectiontableentry2') for i in range(len(trs)): strs='http://xcb.stdu.edu.cn/' link=strs+trs[i].a['href'] r = requests.get(link, headers=headers) content = r.content.decode('utf-8') soup = BeautifulSoup(content, 'html.parser') title=soup.find('h2',class_='contentheading').text.strip() title=title.replace(' ', '') date=soup.find('span',class_='createdate').text.strip()[5:] click=soup.find('span',class_='hits').text.strip()[5:-1] #print(title,date,click) #用来截取文章内容 doc=[] listp=soup.select('div.article-content > p') for j in range(len(listp)): #print(listp[j]) pp='' for k in range(len(listp[j].contents)): if(listp[j].contents[k].name=='span'): pp+=listp[j].contents[k].text elif(listp[j].contents[k].name=='b'): pp+=listp[j].contents[k].span.text doc.append(pp) doc='\n'.join(doc) #print(doc)
②爬取图片链接:
一开始也是以为只是在span里的input标签里,之后爬取10条数据后比较原文发现少了几张图片,于是回头在取分析少的图片,发现原来有的图片在img标签里
然后再爬取image里的图片链接,之后将两个爬取的链接合并,图片链接之间用空格分割
单个网页爬取完成后,分析我要是爬取10页内容,发现他们之间的规律,每翻一页后面就会发生变化:
第一页:http://xcb.stdu.edu.cn/2009-05-05-02-26-33.html
第二页:http://xcb.stdu.edu.cn/2009-05-05-02-26-33.html?start=10
第三页:http://xcb.stdu.edu.cn/2009-05-05-02-26-33.html?start=20
发现start里的数字都是10个10的加,因此我们就可以翻页爬取,然后存入数据库
数据库截图:
戴伟伟:
代码:
import android.content.DialogInterface; import android.os.Bundle; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.navigation.NavController; import androidx.navigation.Navigation; import androidx.navigation.ui.AppBarConfiguration; import androidx.navigation.ui.NavigationUI; import com.google.android.material.bottomnavigation.BottomNavigationView; import com.lh.std_everything.ui.home.hometype.blog.WriteViewModel; public class MainActivity extends AppCompatActivity { private NavController controller; public static String getUserid() { return userid; } private static String userid; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); BottomNavigationView bottomNavigationView=findViewById(R.id.bottomNavigationView); controller = Navigation.findNavController(this,R.id.navhost); AppBarConfiguration configuration=new AppBarConfiguration.Builder(bottomNavigationView.getMenu()).build(); NavigationUI.setupActionBarWithNavController(this, controller,configuration); NavigationUI.setupWithNavController(bottomNavigationView, controller); userid=getIntent().getStringExtra("id"); } MainActivity
public class Login extends AppCompatActivity { private static final String SHARED_FILE_NAME ="USERCOUNT" ; private AutoCompleteTextView search; private EditText pwd; private FloatingActionButton login_btn; private final static String requrl="http://39.98.165.112/STDEverything/login_servlet"; //ip:116.62.178.231 private TextView tip=null; // 要申请的权限 private String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.CALL_PHONE, Manifest.permission.CAMERA,Manifest.permission.ACCESS_COARSE_LOCATION}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); supportRequestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_login); getPower(); initView(); String[] arr = getUserCount(); ArrayAdapter<String> adapter= new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, arr); search.setAdapter(adapter); tip.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(Login.this,Register.class)); } }); login_btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final String name=search.getText().toString(); String userpwd=pwd.getText().toString(); final String reqdata="method=login&name="+name+"&pwd="+userpwd+""; new Thread(new Runnable() { @Override public void run() { try { final JSONObject jsonObject=new JSONObject(String.valueOf(HttpUtil.sendPost(requrl,reqdata)));//子线程获取json结果 runOnUiThread(new Runnable() { //主线程运行跳转活动 @Override public void run() { try { Toast.makeText(Login.this, jsonObject.get("msg").toString(), Toast.LENGTH_SHORT).show(); if(jsonObject.get("status").equals("1")){ //状态码1代表登陆成功 if(!judgeUserExists(name)) recordUser(name); startActivity(new Intent(Login.this, MainActivity.class) .putExtra("id",jsonObject.get("id").toString())); } } catch (JSONException e) { e.printStackTrace(); } } }); } catch (JSONException e) { e.printStackTrace(); } } }).start(); } }); } private void recordUser(String val){ //保存用户账户 Date date=new Date(); SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddhhmmssSSS"); SharedPreferences shf=getSharedPreferences(SHARED_FILE_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor=shf.edit(); editor.putString(sdf.format(date),val); editor.apply(); } Login
public class Register extends AppCompatActivity { private RegisterHandler handler=null; private EditText name_text=null; private EditText pwd_text=null; private final static String requrl="http://39.98.165.112/STDEverything/login_servlet"; //ip:116.62.178.231 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); supportRequestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_register); FloatingActionButton register_btn = findViewById(R.id.register_btn); name_text=findViewById(R.id.name); pwd_text=findViewById(R.id.pwd); handler=new RegisterHandler(this); register_btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String name=name_text.getText().toString(); String pwd=pwd_text.getText().toString(); final String reqdata="method=login&name="+name+"&pwd="+pwd+""; new Thread(new Runnable() { @Override public void run() { try { JSONObject jsonObject=new JSONObject(String.valueOf(HttpUtil.sendPost(requrl,reqdata))); Message msg=new Message(); msg.what=100; msg.obj=jsonObject; handler.sendMessage(msg); } catch (JSONException e) { e.printStackTrace(); } } }).start(); } }); } static class RegisterHandler extends Handler{ private final WeakReference<Register> mcontext; RegisterHandler(Register context){ mcontext=new WeakReference<>(context); } @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); if(msg.what==100){ JSONObject jsonObject= (JSONObject) msg.obj; try { if(jsonObject.get("name").equals("yes")) Toast.makeText(mcontext.get(),"用户名已存在",Toast.LENGTH_SHORT).show(); else if(jsonObject.get("name").equals("no")){ new Thread(new Runnable() { @Override public void run() { String name=mcontext.get().name_text.getText().toString(); String pwd=mcontext.get().pwd_text.getText().toString(); final String reqdata="method=register&name="+name+"&pwd="+pwd+""; HttpUtil.sendPost(requrl,reqdata); sendEmptyMessage(888); } }).start(); } } catch (JSONException e) { e.printStackTrace(); } }else if(msg.what==888){ Toast.makeText(mcontext.get(), "注册成功", Toast.LENGTH_SHORT).show(); mcontext.get().startActivity(new Intent(mcontext.get(),Login.class)); } } } } Register
明天尝试将注册信息存入云服务器数据库。
李浩:网络请求数据(参数+文件)
效果图:
具体功能:
1、显示博客的详细信息,如果有图片可以加载图片(包括用户头像,没有就使用默认头像);
2、可以交互,如点赞、关注、收藏等(包括取消);
3、可以评论,点击👍旁边的气泡即可(今天还没有写,准备明天彻底完善)
4、可以显示用户的评论,格式就像最下方的一样;
总结:我以为今天可以彻底完成的,但是人生总是那么的充满意外,写这些小交互的时候后台函数调用错误,找错误废了不少时间,上述图中,除了不能写评论,其余功能都可以实现,对,包括展示评论,后台全部已经写好,明天写一下客户端就彻底🆗了,最后再说一句,Thread+Handler线程并发真好用。